前言
求打印结果
var a = { n: 1 }
var b = a
a.x = a = { n: 2 }
console.log(a.x)
console.log(b.x)
过程
1. var a = { n: 1 }
- 创建了一个对象
{ n: 1 }
,变量a
保存了该对象的地址。
2. var b = a
- 变量
b
被赋值为a
,b
引用了和a
相同的对象{ n: 1 }
。
3. a.x = a = { n: 2 }
这一步是最关键的,它包含了两次赋值操作,JS 赋值操作的顺序是从右到左的。
-
首先执行右边的
a = { n: 2 }
- 创建了一个新的对象
{ n: 2 }
,此时a
不再指向最初的{ n: 1 }
,而是指向新的对象{ n: 2 }
。
- 创建了一个新的对象
-
然后执行
a.x = ...
- 在赋值
a = { n: 2 }
之前,a
仍然指向最初的{ n: 1 }
对象,因此这一部分实际上是对原始对象a
(即{ n: 1 }
)的操作。 a.x = ...
这里的a
仍然指向{ n: 1 }
,所以等效于:{ n: 1 }.x = { n: 2 }
,因此在旧的对象{ n: 1 }
上添加了一个属性x
,其值是{ n: 2 }
。
- 在赋值
到这一步,内存中的对象情况如下:
a
:指向{ n: 2 }
(新对象)。b
:仍然指向{ n: 1, x: { n: 2 } }
(旧对象)。
4. console.log(a.x)
- 由于
a
现在已经是新的对象{ n: 2 }
,这个对象中并没有x
属性,因此输出是undefined
。
5. console.log(b.x)
b
仍然指向旧对象{ n: 1, x: { n: 2 } }
,并且在第三步中我们已经在该对象上设置了x
属性,其值为{ n: 2 }
,所以输出是{ n: 2 }
。
总结:
- 在
a.x = a = { n: 2 }
中,赋值操作是从右向左的,先执行a = { n: 2 }
,然后对旧对象a
(现在是b
引用的对象)进行赋值a.x = { n: 2 }
。 - 因此,
a
指向的新对象没有x
属性,而b
仍然指向旧对象,旧对象上有x
属性。