连续赋值与成员访问运算符优先级

一道比较复杂的 JavaScript 的题,如下:

1
2
3
4
5
var a = {n: 1};
var b = a;
a.x = a = {n: 2};

console.log(a.x, b.x);

猜一下结果是什么?

分析一下上面的代码:

var a = {n: 1};
var b = a;

这两行代码很简单,创建了一个对象,假设叫做 OBJ1,OBJ1 中有一个属性 n,值为 1.
定义了两个变量 a 和 b,同时指向对象 OBJ1。

本题的难点在于下一行代码。

a.x = a = {n: 2};

这是连续赋值的代码。
在 JavaScript 中,JavaScript 解释器会从右到左去执行连续赋值的代码,比如 var a = b = 2; ,JavaScript 解释器会先执行 b = 2,然后执行 var a = b;

上面的代码,很容易被理解成先执行 a = {n: 2}, 即创建一个新的对象 OBJ2,将 a 重新指向 OBJ2,然后执行 a.x = a,即为 OBJ2 添加一个属性 x,并将属性 x 也指向 OBJ2。
这样的话,a.x 将得到对象 OBJ2,b.x 得到 undefined。然而,运行后发现,结果并不是如此。

题目分析

运行后发现,本题的结果是:

undefined Object

这与刚才的分析结果正好相反。

这是因为,该语句中,成员访问运算符拥有最高的优先级。因此,该语句的执行顺序是:

a.x = a = {n: 2};
  • 先执行 a.x ,为对象 OBJ1 添加属性 x,但是不赋值
  • 然后按照连续赋值的顺序,执行 a = {n: 2},创建新的对象 OBJ2,OBJ2 拥有属性 n,值为 2,并将变量 a 重新指向 OBJ2。
  • 最后执行 a.x = a,因为第一步中已经将表达式 a.x 解释过,指向对象 OBJ1 的属性 x,所以此时不再解释 a.x,而是直接将 a 的值赋给对象 OBJ1 的属性 x。

此时,变量 a 指向对象 OBJ2,OBJ2 含有一个属性 n, 值为 2.
变量 b 仍旧指向对象 OBJ1,OBJ1 含有两个属性,属性 n 值为 1, 属性 x 指向对象 OBJ2。

这样,就可以理解本题为什么得到 undefined object 了。 a.x 会在 OBJ2 中寻找属性 x ,找不到则去原型链中寻找,如果一直找不到则返回 undefined。 b.x 得到 OBJ1 的属性 x 的值,即对象 OBJ2。

注意:本题涉及到了两个知识点:一是连续赋值语句,二是成员访问运算符的优先级。

运算符优先级

下面是 JavaScript 中运算符的优先级,按照优先级的不同从高到底排列:

image
image
image
image
image

-------------本文结束感谢您的阅读-------------
Fork me on GitHub