【问题标题】:What's the result of this JavaScript code snippet, and why?这个 JavaScript 代码片段的结果是什么,为什么?
【发布时间】:2018-02-27 03:20:04
【问题描述】:

我是 JavaScript 的新手,刚刚遇到了这个问题。通过谷歌搜索和在stackoverflow上搜索无法弄清楚。代码sn-p如下:

var a = { n : 1};    
var b = a;    
a.x = a = {n:  2};    
console.log(a.x);
console.log(b.x);

根据我目前的知识,a.x = a = {n:2}; 等于:

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

最终使 a 等于 {n:2, x:{n:2}}。所以a.x应该等于{n:2},因为b = a,所以b.x = {n:2}。 但我在浏览器中运行的结果是:alert(a.x)undefinedalert(b.x)[object object]

有人可以解释为什么吗?非常感谢。

【问题讨论】:

    标签: javascript


    【解决方案1】:

    这是运算符优先级的问题,问题在于这里涉及到三个运算符:==.

    . 的运算符优先级高于=,这意味着a.x 在分配发生之前被评估。之后,在语句中间重新分配a 不会影响表达式a.xa 的值,因为它已经被计算过了。

    我们可以通过以下代码观察到这一点:

    var _a;
    var logging = false;
    Object.defineProperty(window, 'a', { 
        get: function () {
            if (logging) {
                console.log('getting a'); 
            }
            return _a; 
        }, 
        set: function (val) { 
            if (logging) {
                console.log('setting a');
            }
            _a = val; 
        }
    });
    
    a = { n : 1};    
    var b = a;
    
    logging = true;
    a.x = a = {n:  2};
    logging = false;
    
    console.log(a.x);
    console.log(b.x);

    我们在这里可以看到a 的getter 在我们正在讨论的语句过程中它的setter 之前被访问,并且它只被访问一次。这告诉我们a.x 正在被评估之前 a.x = ....

    你的错误在于该语句实际上是这样计算的:

    a = {n:  2}
    [the value that a referred to before the statement started executing].x = {n : 2};
    

    换句话说,标识符a在语句开始执行之前是“绑定的”,并且属性引用将在语句的持续时间内引用该绑定值。

    在这种情况下,[the value that a referred to before the statement started executing] 等价于b,但不等价于a 的新值。这就是为什么b.x 有值而a.x 没有。

    你可以说这个过程是这样的:

    var a = { n : 1};    
    var b = a;
    
    var temp = a;
    var newVal = {n: 2};
    a = newVal;
    temp.x = newVal;
    
    console.log(a.x);
    console.log(b.x);

    这会产生与原始代码相同的结果。

    ECMAScript Spec 列出了评估 LeftHandSideExpression = AssignmentExpression 形式的 AssignmentExpression 的过程。前两个步骤是:

    1. 设 lref 为计算 LeftHandSideExpression 的结果。
    2. 设 rref 为评估 AssignmentExpression 的结果。

    这清楚地表明,赋值的左侧在评估之前右侧评估。

    【讨论】:

    • 这与优先级(或关联性)无关。相反,它是计算操作数的顺序,特别是操作数a.xa = {n: 2}。结果令人惊讶(对我来说)的原因是= 是右关联的,但仍然从左到右评估操作数。除了那个挑剔之外,你的答案是正确的,而且信息量很大。
    【解决方案2】:

    代码

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

    一样
    var a = b = 1;
    

    变量 a 和 b 都将设置为 1;

    现在,由于您正在使用一个对象并为其自身分配一个值(您实际上是在创建一个循环,但 JS 中的例程仍然很混乱(它应该这样做)。

    基本上问题在于评估的顺序。

    这只是我在试验后的看法。不过,我想听听社区的其他答案。

    【讨论】:

      【解决方案3】:

      您遇到了一个名为operator precedence/associativity 的东西。

      考虑他们的例子:

      a = b = 5;
      

      a 和 b 得到值 5 的预期结果。这是因为 赋值运算符返回分配的值。首先,b 设置为5。那么a也设置为5,b的返回值=5, 也就是赋值的右操作数。

      这分解为:

      b = 5
      a = the return of b which is now 5.
      
      a.x = a = {n:  2};
      

      分为:

      a = {n: 2}
      a.x = the return of a = {n: 2}
      

      由于 b 是对 a 的引用,因此在赋值后会更改。

      【讨论】:

      • 您在此处链接的文档说:Assignment operators are right-associative
      • 呵呵,看来你是对的。赋值操作具有右结合性
      • 您的回答没有解释为什么a.xundefined
      【解决方案4】:

      a.x 在这种情况下是未定义的,因为

      由于第二次赋值 (a = {n:2}),a.x 中的引用已更改为新的引用值

      使用旧引用读取属性的值未定义,因为它已更改

      要查看差异添加新属性,类似这样

      a.x = a.y = {n:2} // 这不会改变 a 的引用

      console.log(a.x);//{n:2}
      console.log(a.y);//{n:2}
      console.log(b.x);//{n:2}
      

      https://codepen.io/nagasai/pen/JpeENv

      来自类似问题的更多详细信息 - How does a.x = a = {n: b} work in JavaScript?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-05-24
        • 2015-04-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-07-13
        • 1970-01-01
        相关资源
        最近更新 更多