先看下面这个问题:
var a = {n: 1};
var b = a;
a.x = a = {n: 2};
console.log(a.x); // --> ?
console.log(b.x); // --> ?
要解决这个问题,需要理解:
- JS引擎对赋值表达式的处理
- 赋值运算的右结合性
赋值表达式
形如
A = B
的表达式称为赋值表达式,其中A和B又分别可以是表达式。B可以是任意表达式,但是A必须是一个左值。
左值:可以被赋值的表达式,在ES规范中是用内部类型引用(Reference)描述的。
JS引擎会按如下步骤处理赋值表达式:
- 计算表达式A,得到一个引用
ARef
; - 计算表达式B,得到一个引用
BRef
; - 通过
GetValue(BRef)
,得到一个值BValue
- 解析
ARef
,如满足一定条件(不是左值的条件),会报SyntaxError
异常 - 通过
PutValue(ARef, BValue)
进行赋值 - 返回
BValue
赋值运算的右结合性
赋值表达式是右结合的。即:
Exp1 = Exp2 = Exp3 = Exp4
等价于
Exp1 = (Exp2 = (Exp3 = Exp4))
连等赋值解析
根据上面两部分的知识,连等赋值的解析如下:
=> Exp1 = (Exp2 = (Exp3 = Exp4))
=> Ref1 = (Ref2 = (Ref3 = Ref4))
=> Ref1 = (Ref2 = (Ref3 = GetValue(Ref4)))
=> Ref1 = (Ref2 = (Ref3 = Value4))
=> Ref1 = (Ref2 = Value4)
=> Ref1 = Value4
=> Value4
总结一下就是:先从左到右解析各个引用,然后计算最右侧的表达式的值,最后把值从右到左赋给各个引用。
现在回到开头的问题,前两个var语句执行完后,a
和b
都指向同一个对象{n: 1}
,对于
a.x = a = {n: 2};
先从左到右先解析各个引用,因此a.x
中的a
指向的是对象{n: 1}
,b
和此时的a
指向的是同一个对象
接着a
被重新赋值为{n: 2}
,再然后a.x
被赋值{n: 2}
,于是得到
console.log(a.x) // undefined
console.log(b.x) // {n: 2}
参考
由ES规范学JavaScript(二):深入理解“连等赋值”问题
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。