【问题标题】:Why is this an invalid assignment left hand side?为什么这是一个无效的分配左侧?
【发布时间】:2017-03-27 15:54:54
【问题描述】:

为什么可以进行以下操作:

var b1, b2;

b1 = b2 = true;

document.write(b1," ", b2);
document.write("<br>");

b1 = !b2;

document.write(b1," ", b2);
document.write("<br>");

b1 = b2 = !true;

document.write(b1," ", b2);

然而,当我尝试以下操作时,我收到了 ReferenceError: invalid assignment left-hand side

var b1, b2;

b1 = !b2 = true;

document.write(b1," ", b2);

很明显我不能这样做,但我找不到解释为什么我不能这样做。错误状态的MDN developer guide

某处有意外的任务。例如,这可能是由于赋值运算符和比较运算符不匹配造成的。当单个“=”符号为变量赋值时,“==”或“===”运算符比较一个值。

所有的赋值操作符都被证明是单独工作的,那么为什么不能把它组合成一个单一的操作/链式赋值呢?

【问题讨论】:

  • 实际上,您正在尝试这样做:var b1, b2; !b2 = true; b1 = b2; 这显然是错误的。您不能将表达式分配给值...
  • b2 不是一个检查它是真还是假的函数,你应该把它放在括号里,就像你做一个条件链引用一样,如果它们有相同的数据,它们就可以是真的

标签: javascript language-lawyer


【解决方案1】:

表达式b1 = !b2 = true; 从右到左求值

第一:!b2 = true

然后:b1 = &lt;result of previous assignment&gt;

!b2 = true 没有逻辑意义,因此出现错误。

如果你写b1 = b2 = true;,就不会报这样的错误

【讨论】:

  • 这个表达式根本不会被计算,因为它在语法上是无效的并且在解析时会抛出一个早期的错误。
【解决方案2】:

当您尝试这样做时:

var b1, b2;

b1 = !b2 = true;

document.write(b1, " ", b2);

因为它们在功能上是等效的你基本上是在做:

var b1, b2;

!b2 = true;
b1 = true; //just the value of b2, not b2 itself

document.write(b1, " ", b2);

!b2 = true 行中,您正试图将计算结果为一个值(左侧)的表达式分配给一个值——这绝对没有意义。这样想:

  • !b2 被分配给 true!b2 是一个表达式,被评估为一个布尔 ,而不是变量。
  • 这类似于1 + 1 = 2。由于1 + 1 被评估为一个,因此您不能将它分配给另一个值2。您必须为 variable 赋值,因为值到值的赋值在语义和逻辑上都是无效的。
  • 考虑上述问题的另一种方法是实现这一点:1 + 1 是一个值。 2 是一个值。您不能将值分配给值,因为该值已经具有值。 2 等常量的值为 2,不能更改。如果我们尝试1 - 1 = 2 会怎样? 0,一个常量和值,不能是2,因为它是一个常量。

因此,将一个值赋给一个值在语义上和逻辑上都是无效的。您不能将0 分配给2,就像您不能将false 分配给true

如果您想更好地理解语法和语义,以及为什么会抛出 ReferenceError,您可以深入研究 ECMAScript® 2015 Language Specification。根据规范:

Section 12.14.1 - 赋值运算符 - 静态语义:早期错误

AssignmentExpression : LeftHandSideExpression = AssignmentExpression

  • 如果 LeftHandSideExpression 既不是 ObjectLiteral 也不是 ArrayLiteral 和 @987654354,这是一个早期的 Reference 错误LeftHandSideExpression 的@ 是假的。

IsValidSimpleAssignmentTarget 在哪里:

Section 12.14.3 - 赋值运算符 - 静态语义:IsValidSimpleAssignmentTarget

AssignmentExpression :
  YieldExpression
  ArrowFunction
  LeftHandSideExpression = AssignmentExpression
  LeftHandSideExpression AssignmentOperator AssignmentExpression

1。返回 false。

现在回头看看你的代码:b1 = !b2 = trueb1 = !b2 很好,因为它是 LeftHandSideExpression = AssignmentExpression,因此对于 IsValidSimpleAssignmentTarget 返回 true。当我们检查!b2 = true 时,问题就出现了。如果我们看LeftHandSideExpression的定义:

Section 12.3 - 左侧表达式

语法

LeftHandSideExpression :
  NewExpression
  CallExpression

(您可以在上面的规范链接中查看NewExpressionCallExpression的定义)

您可以看到!b2 = true 不是有效的AssignmentExpression,因为它不符合条件LeftHandSideExpression = AssignmentExpression。这是因为!b2 不是有效的LeftHandSideExpression,也不是ObjectLiteral 也不是ArrayLiteral,因此IsValidSimpleAssignmentTarget返回 false,抛出 ReferenceError。请注意,错误是early error,这意味着它在执行任何代码之前被抛出,如@Bergi's comment 中所述。


您可以通过执行以下任一操作来解决此问题,具体取决于您想要的结果:

b1 = !(b2 = true);

使用括号,括号内优先于括号外。这样,b2 被赋值,因为它是true,括号内的计算结果为true。接下来就相当于:

b1 = !(true);

如上所述,括号内的计算结果为trueb1 将与预期的 b2 相反,b2 将是 true

如果您希望 b1 成为 trueb2 成为 false,请像这样重组语句:

b2 = !(b1 = true);

这样一来,与上面的完全相反,给出b1 = trueb2 = false


正如@Bergi 在 cmets 中提到的,b1 被分配了正确的操作数,在这种情况下为 true,而不是 !b2

虽然目前大多数浏览器不支持 ECMAScript 6 (2015) 的所有功能,而是使用ECMAScript 5.1 (2011),但两个版本的规范是相同的。所有定义都相同,因此解释仍然有效。

【讨论】:

  • @DavidTheWin 你确定吗? b1 = !b2 = true 似乎他们希望!b2 为假,这意味着b2 应该为真而b1 为假。
  • 我把它读作“b1 和 !b2 都是真的”,而 b2 是假的
  • 其实是b1 = true;而不是b1 = b2;。赋值操作的结果只是右操作数,而不是对左手边求值。对于普通的词法变量无关紧要,但对于更复杂的赋值目标则可以。
  • @Bergi 是的,我提到它们在这种情况下在功能上是等效的。我会相应地编辑,谢谢!
  • @DavidTheWin 的初衷是让 b1 为假,b2 为真。该示例当前是正确的。
【解决方案3】:

只是提一下 AST 分配给右值 (1) (2)

if (1 + 1 = 2)
 console.log("1 + 1 = 2");

var b1, b2;
b1 = !b2 = true;

确认表达式永远不会被计算

【讨论】:

    【解决方案4】:
    b1 = b2 = true;
    

    等价于

    b2 = true;
    b1 = true;
    

    赋值返回正确的操作数。在交互式控制台中很容易看到(如Chrome DevToolsNodeJSjsc)。请参阅spec details in Andrew's answer

    当您尝试b1 = !b2 = true; 时,等效的没有意义:

    (!b2) = true; // true = true; causes the error.
    b1 = b2;      // never evaluated.
    

    这是因为 != 赋值运算符上采用precedence,如(!b2) 中的括号所示。

    顺序如下:

    1. b2undefined,因为它尚未初始化。
    2. 然后!b2 === true!undefined === true,所以!b2 变为true
    3. 然后分配发生,所以true = true

    您可以通过添加括号使其按预期工作:

    b1 = !(b2 = true);
    

    【讨论】:

    • 感谢您的回答,尤其是对优先级的引用。我不明白:!b2 = true; // true = true;为什么我还没有分配b2时是true = true
    • @Zze 因为一开始是b2 === undefined。然后!b2 === true变成!undefined === true,所以!b2变成true,然后赋值发生,所以true = true
    • 其实是b1 = true;而不是b1 = b2;。赋值操作的结果只是右操作数,而不是对左手边求值。对于普通的词法变量无关紧要,但对于更复杂的赋值目标却可以。
    • @Bergi 考虑这个例子:var b1, b2; b1 = b2 = [],正如您的评论,b1b2 似乎应该引用不同的数组,但它们引用的是同一个数组,所以我认为 @ 987654357@ 是正确的。还是我错过了什么?
    • @dashtinejad 不,我从未说过要重新评估正确的操作数。我的观点是 b2 从来没有被用来提供价值,它只是一个分配的目标。实际上发生的事情更像是{ const temp []; b2 = temp; b1 = temp; }
    猜你喜欢
    • 1970-01-01
    • 2018-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-07
    • 1970-01-01
    相关资源
    最近更新 更多