最近在GitHub上发现一个有意思的问题,如下:
var foo = {n: 1};
var bar = foo;
foo.x = foo = {n: 2};
console.log(foo.x); // undefined
console.log(bar); // Object {n: 1, x: {n:2}}
运行结果显示 foo.x 的值为 undefined,分析过程如下:
ECMAScript 规范指出:
- Let lref be the result of evaluating LeftHandSideExpression.
- ReturnIfAbrupt(lref).
- Let rref be the result of evaluating AssignmentExpression.
- Let rval be GetValue(rref).
[…]
大致意思是,对于 类似 A=B 的表达式,首先计算A表达式,得到 lref,然后计算计算表达式 B,
得到 B 的值 rval,将 rval 赋给 lref 指向的变量,返回 rval。
JS的赋值表达式是右结合的,所以 foo.x = foo = {n:2} 等价于 foo.x = ( foo = { n : 2} )
根据计算顺序,
- 计算出括号内的值,然后赋给 foo.x。
- 得到 foo.x = { n : 2 },同时 bar.x = { n : 2 } ,左边第一个等号的计算结束。
- 然后计算括号内的表达式,即 foo = { n : 2 }。
- foo 被赋予新的对象,不再是原来对象的引用,指向了 { n : 2 },所以foo.x
得到的是 undefined, 但是 bar 依旧指向原来的对象,所以bar值没有改变。
此段代码等同于如下的代码:
var foo = {n: 1};
var bar = foo;
var foo1 = {n: 2};
foo.x = foo1;
foo = foo1;
console.log(foo.x,bar);