JS下的链式赋值

今天看到群里发了一道JS基础题,以为很简单,但是不小心还做错了,于是这里Mark下。

1
2
3
4
5
6
7
var user = {
n: 1
};
user.x = user = {
n: 2
};
console.log(user.x);

错误分析

错误的以为等价于如下代码

1
2
3
4
5
6
7
8
9
10
11
var user = {
n: 1
};
user = {
n: 2
};
user.x = user;
console.log(user.x);
/**
* {n: 2, x: {…}}
* /

浏览器或者Node下执行,发现正确结果应该是undefined

继续错误分析

我重新翻了下JS的赋值运算符。针对链式赋值

Chaining the assignment operator is possible in order to assign a single value to multiple variables

也就是说实际上JS做的事是将一个值分别赋给了多个变量。即a=b=c应该等价于b=c;a=c;

因此上述代码等价代码如下

1
2
3
4
5
6
7
8
9
10
var user = {
n: 1
};
user = {
n: 2
};
user.x = {
n: 2
};
console.log(user.x);

心想,这回总该对了吧,浏览器运行发现结果是{n: 2}还是不对,呃呃呃,问题在哪?- 成员访问点运算符。

正确分析

成员访问点运算符的优先级是最高的

因此重新再分析代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var user = {
n: 1
};

user.x = {
n: 2
};

user = {
n: 2
};
console.log(user.x);

/**
* user.x这时指向的是一开始的user,将其看成1
* {n:2} 作为新的user,即2
* {n:2}赋值给1的x
* 最后打印2的x,没有这个属性,因此undefined
*
* 其实还是先user后user.x但是不一个user
*/

这么简单的题,说实话,挺坑的。不过也暴露了基础不扎实,同时自信品品这道题好几个细节点。

  1. 点运算法符优先级高于赋值运算符

  2. 链赋值基本逻辑是1个值同时赋给多个变量,并且赋值顺序是自右向左,这点其实也可以做个实验来证明,比如使用Proxy代理对象即可

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    var superman = {
    name: 'clark'
    };

    var batman = {};
    var flashman = {};

    const batmanProxy = new Proxy(batman, {
    set: function (target, key, value) {
    console.log(`batman: ${key} set from ${target[key]} to ${value}`);
    target[key] = value;
    return true;
    }
    });

    const flashmanProxy = new Proxy(flashman, {
    set: function (target, key, value) {
    console.log(`flashman: ${key} set from ${target[key]} to ${value}`);
    target[key] = value;
    return true;
    },
    get: function (target, key) {
    console.log(`flashman: ${key} get from ${target[key]}`);
    return target[key];
    }
    });

    batmanProxy.name = flashmanProxy.name = superman.name;

    以上代码打印会发现,首先被调用的set方法是flashman的,同时注意,以上并不等价于batmanProxy.name = flashmanProxy.name = superman.name;因为flashmanProxy的get方法并没被调用

  3. 对象赋值是地址赋值

写在最后

问题简单但很基础,继续学习,继续搬砖。