连续赋值问题

  • 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};这段代码的执行顺序遵循如下原则:

  1. 先获取等号左侧的a.x,但a.x并不存在,于是JS为(堆内存中的)对象创建一个新成员x,这个成员的初始值为undefined

这也是为什么直接引用一个未定义的变量会报错,但是直接引用一个对象的不存在的成员时,会返回undefined

  1. 创建完成后,目标指针已经指向了这个新成员x,并会先挂起,等待等号右侧的内容有结果了,再完成赋值。

  2. 接着执行a = {n:2};发现这是个简单的赋值语句,于是将堆中a存储的值修改为了{n:2}

这里需要特别注意,这个a已经不是开头的那个a,而是一个全新的a。这个新a指针已经不是指向原来的值的那个堆内存,而是分配了一个新的堆内存。但是原来旧的堆内存因为还有b在占用,所以并未被回收。

  1. 等待上述语句执行完成后就执行之前等待的命令,将新生成a的索引地址赋值给了x,因此x的值也就变成了{n:2}

  2. 等待上述代码执行完成后,此时b = { n:1, x: { n:2 }},a = { n: 2}

因此也就有了上述的代码结果。

TIP

总结:

  1. 基础类型的存储是存在栈中的,引用类型的存储是在栈中存储堆的索引,在堆中开辟空间存储变量的值。

  2. 当访问一个对象的属性的时候,如果属性不存在会被赋值为undefined

  3. 当进行连续赋值的时候,.属性访问优先级会大于=赋值,且当成员等待赋值的时候,锁定的赋值目标是成员,而非对象。

  4. 对象重新赋值时,并非是修改原堆内存的值,而是重新分配堆内存,栈内存中的指针会做相应修改。

  5. 如果原堆内存有多个栈内存指向它,由于引用还存在,原堆内存的数据不会消失。如果堆内存再无其它引用,则会被JS的垃圾回收机制回收。对象的成员对象也一样。

上次更新时间: 2021-04-26 20:43:00