问题
先看下面这个问题:
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(二):深入理解“连等赋值”问题