搞懂JavaScript中的连续赋值

前段时间老是被一道题刷屏,一个关于连续赋值的坑。
遂留下一个笔记,以后再碰到有人问这个题,直接丢过去链接。。

题目代码:

let a = { n: 1 }
let b = a

a.x = a = { n: 2 }

console.log(a.x) // => undefined
console.log(b.x) // => { n: 2 }

首先解释一下连续赋值的意思:
表达式variable = 1,这个为赋值语句。
当我们要给多个变量进行赋值时,有一个简单的写法。
variable1 = variable2 = 1,这个我们就称之为连续赋值。

再来说上边的那道题,我一次看到这个题的时候,答案也是错了,后来翻阅资料,结合着调试,也算是整明白了-.-

前两行的声明变量并赋值,使得ab都指向了同一个地址({ n: 1 }在内存中的位置)

为了理解连续赋值的运行原理,我们需要结合着ECMAScript的文档来解释一下=赋值的执行过程

image

图中出现了一个关键字LeftHandSideExpression(我们简称为LHS
MDN对该关键字的解释为“Left values are the destination of an assignment.”,翻译过来大概就是:LHS是用来分配赋值操作结果存放的位置(也就是=右边的这坨东西要放到哪)。

在执行一个赋值操作时,我们首先要取出=左侧的变量,用来确定这次赋值操作最终结果的存放位置。
然后运算=右侧的表达式来获取最终的结果,并将结果存放入对应的位置,也就是前边取出的变量所对应的位置。

再来说连续赋值,其实就是多次的赋值操作。

我们从代码的第一行开始,画图,一个图一个图的来说:

  1. let a = { n: 1 }声明了一个变量a,并且创建了一个Object{ n: 1 },并将该Object在内存中的地址赋值到变量a中,这时就能通过a来获取到{ n: 1}引用类型的值是只存放地址的,而不是直接存放原始值({} !== {}
    image
  2. let b = a声明一个变量b,并且将a赋值给b,这时,ab都指向了{ n: 1 }
    image
  3. 执行表达式(a.x = a = { n: 2 }),取出a.x的位置,由于a的值为{ n: 1 },所以取属性xundefined,遂在内存中开辟一块新的空间作为({ n: 1}).x的位置:
    image
  4. 执行剩余表达式(a = { n: 2 }),取出a的位置,因为a是一个已声明的变量,所以该步骤并不会有什么改变;
  5. 执行剩余表达式({ n: 2 }),为{ n: 2 }在内存中开辟一块空间存放数据:
    image
  6. { n: 2 }赋值到第4步取出的a对应的位置:
    image
  7. { n: 2}赋值到第3步取出的a.x对应的位置:
    image

这时我们就完成了整个赋值步骤:

  • 变量a指向{ n: 2 }
  • 变量b指向{ n: 1, x: { n: 2} }
  • 也就是说a === b.x

小记

该代码坑就在于:赋值运算会在运算=右侧前就取出了要赋值的位置,而不是获得结果后再去取出赋值位置的。
先取位置,后赋值
所以说,看文档很重要 很重要 很重要

参考资料