连续赋值问题
- 2021.04.12
关于javascript连续赋值问题,我们先来看一段代码:
let a = {n : 1};
let b = a;
a.x = a = {n: 2};
console.log(a.x) // undefined
console.log(b.x) // {n:2}
console.log(a === b.x) // true
首先let a = {n : 1};定义了一个引用类型变量a,在栈中存储了一个变量a的内存地址,在堆(内存)中开辟了一个空间存放对应的值{n:1}。
其次let b = a;在栈中写入了b,并将a指向堆中的地址索引赋值给了b。但是并没有开辟新的存储空间。
接下来执行a.x = a = {n:2};这段代码的执行顺序遵循如下原则:
- 先获取等号左侧的
a.x,但a.x并不存在,于是JS为(堆内存中的)对象创建一个新成员x,这个成员的初始值为undefined。
这也是为什么直接引用一个未定义的变量会报错,但是直接引用一个对象的不存在的成员时,会返回undefined。
创建完成后,目标指针已经指向了这个新成员x,并会先挂起,等待等号右侧的内容有结果了,再完成赋值。
接着执行
a = {n:2};发现这是个简单的赋值语句,于是将堆中a存储的值修改为了{n:2}。
这里需要特别注意,这个a已经不是开头的那个a,而是一个全新的a。这个新a指针已经不是指向原来的值的那个堆内存,而是分配了一个新的堆内存。但是原来旧的堆内存因为还有b在占用,所以并未被回收。
等待上述语句执行完成后就执行之前等待的命令,将新生成
a的索引地址赋值给了x,因此x的值也就变成了{n:2}。等待上述代码执行完成后,此时
b = { n:1, x: { n:2 }},a = { n: 2}。
因此也就有了上述的代码结果。
TIP
总结:
基础类型的存储是存在栈中的,引用类型的存储是在栈中存储堆的索引,在堆中开辟空间存储变量的值。
当访问一个对象的属性的时候,如果属性不存在会被赋值为
undefined。当进行连续赋值的时候,
.属性访问优先级会大于=赋值,且当成员等待赋值的时候,锁定的赋值目标是成员,而非对象。对象重新赋值时,并非是修改原堆内存的值,而是重新分配堆内存,栈内存中的指针会做相应修改。
如果原堆内存有多个栈内存指向它,由于引用还存在,原堆内存的数据不会消失。如果堆内存再无其它引用,则会被JS的垃圾回收机制回收。对象的成员对象也一样。
← 数据类型转换 toString()方法 →