引用类型、内存原理与对象

一、内存两大区域:栈内存 Stack、堆内存 Heap

计算机给 JS 划分两块地方存放数据,分工完全不一样。

  1. 栈内存(Stack)

空间小,读写速度快

存放两类东西:

基本类型的真实值

引用类型的内存地址(编号),不是对象本身!

栈的变量生命周期简单:函数执行完毕、变量失效,直接出栈销毁。

  1. 堆内存(Heap)

空间很大,读写速度比栈慢

只存引用类型真正的数据:对象、数组,一大坨键值对。

最最重要一句话:

引用类型的对象本体永远待在堆;栈里面的变量,只拿着堆的地址编号,相当于门牌号。

举个例子代码:

js

运行

let user = { name: "小明", age: 18 }

内存发生的事情:

JS 在堆内存开辟一块空间,存入完整对象 { name:"小明", age:18 },假设这块堆的地址编号:0xaa01

在栈内存创建变量 user,变量 user 里面不存对象,只存地址 0xaa01

当写 user.name:JS 读取栈中 user 的地址 0xaa01,拿着地址去堆里面找到真实对象,读取 name 属性。

形象比喻:

堆 = 一套真实房子;栈变量 = 写着房子门牌号的小纸条。

user 手里只有纸条(地址),房子本体在堆。访问属性就是拿着纸条去找房子。

二、基本类型 vs 引用类型赋值本质区别

  1. 基本类型:赋值 = 复制一份真实的值(完全拷贝)

js

运行

let a = 100

let b = a

b = 200

console.log(a) // 100

内存过程:

栈:a = 100

let b = a:把 100 这个值复制一份给 b,栈里出现两份独立数据

修改 b=200,只改动 b 自己的数据,a 不受任何影响。

两个完全独立副本,井水不犯河水。

  1. 引用类型:赋值 = 复制地址纸条!不会复制堆里面的对象!

js

运行

let u1 = { name: "小明" }

let u2 = u1

u2.name = "小红"

console.log(u1.name) // 小红

// ② 给变量直接赋全新对象:给u2换一张新门牌号,断开和u1的联系

u2 = { name: "小李" }

①:改房子里面家具 → 所有人看见变化

②:直接换一套新房子,旧房子不受影响。

三、浅拷贝、深拷贝:如何真正产生新对象

需求:不想共用同一个堆对象,想要两个独立对象。

浅拷贝:只复制对象第一层,生成新堆对象;嵌套对象依旧共用地址

js

运行

let obj1 = { a: 10, info: { age: 18 } }

// 浅拷贝写法1:展开运算符

let obj2 = { ...obj1 }

// 浅拷贝写法2 Object.assign

// let obj2 = Object.assign({}, obj1)

内存行为:

堆开辟新对象给 obj2,复制第一层 a:10;

但是里面的 info 是对象,只复制 info 的地址,info 对象还是原来堆里那一个。

js

运行

obj2.a = 999

console.log(obj1.a) //10 第一层互不干扰

obj2.info.age = 999

console.log(obj1.info.age) //999 嵌套对象还是共享堆

浅拷贝只能解决一层,嵌套引用依旧存在。

深拷贝:所有层级全部新建堆对象,彻底切断引用

简单快速方案(局限性:不能拷贝函数、undefined、symbol)

js

运行

let obj2 = JSON.parse(JSON.stringify(obj1))

相关推荐
inksci5 分钟前
发布Ai生成的html markdown 推荐
前端·javascript·html
fensnote10 分钟前
西门子 S7 PLC 内部寄存器读写 Demo(C 语言)
c语言·开发语言
Lank_M11 分钟前
PDF签完名还能改什么?会签能过,压缩不行
前端
补码缩写15 分钟前
压缩前查PDF签名,字节搜索会漏掉对象流
前端
星光不问赶路人15 分钟前
Vue 项目类型检查:深入理解 vue-tsc --build
前端·vue.js
修炼的dance17 分钟前
压完的PDF验签不报错,是因为签名被删了
前端
ModStart21 分钟前
ProcHub v0.7.0 命令行工具上线,进程、配置与 WSL 都能在终端管理
前端
那年那棵树23 分钟前
基于WebGis的人生如旅地图系统
前端·arcgis·vue·课程设计
ModStart23 分钟前
3 条命令,ProcHub 在终端管好后台进程
前端