先看下面这个问题:

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引擎会按如下步骤处理赋值表达式:

  1. 计算表达式A,得到一个引用ARef
  2. 计算表达式B,得到一个引用BRef
  3. 通过GetValue(BRef),得到一个值BValue
  4. 解析ARef,如满足一定条件(不是左值的条件),会报SyntaxError异常
  5. 通过PutValue(ARef, BValue)进行赋值
  6. 返回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语句执行完后,ab都指向同一个对象{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(二):深入理解“连等赋值”问题

12.3 Left-Hand-Side Expressions

11.13.1 Simple Assignment ( = )


tanyioO
12 声望0 粉丝