为什么这是一个无效的任务左手边?

时间:2016-11-14 03:33:09

标签: javascript language-lawyer

为什么我可以执行以下操作:



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);
&#13;
&#13;
&#13;

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

&#13;
&#13;
var b1, b2;

b1 = !b2 = true;

document.write(b1," ", b2);
&#13;
&#13;
&#13;

很明显,我无法做到这一点,但我无法找到解释为何我无法解释。错误的MDN developer guide表示:

  

某处有意外的任务。例如,这可能是由于赋值运算符和比较运算符的不匹配造成的。单个&#34; =&#34; sign为变量赋值,&#34; ==&#34;或&#34; ===&#34;运营商比较一个值。

所有赋值运算符都经过验证,因此为什么不能将它组合成单个运算/链式赋值?

4 个答案:

答案 0 :(得分:70)

当你尝试这样做时:

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。您必须为变量分配一个值,因为值到值赋值在语义上和逻辑上都是无效的。
  • 考虑上述问题的另一种方法是实现这一点: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 也不是 {{1},这是一个早期Reference错误 ArrayLiteral IsValidSimpleAssignmentTarget为false。
  •   

LeftHandSideExpression的位置:

  

Section 12.14.3 - 分配运算符 - 静态语义:IsValidSimpleAssignmentTarget

IsValidSimpleAssignmentTarget
     

1。返回false。

现在回顾一下你的代码:AssignmentExpression : YieldExpression ArrowFunction LeftHandSideExpression = AssignmentExpression LeftHandSideExpression AssignmentOperator AssignmentExpression b1 = !b2 = true很好,因为它是 b1 = !b2 ,因此LeftHandSideExpression = AssignmentExpression返回true。当我们检查IsValidSimpleAssignmentTarget时会出现问题。如果我们查看 !b2 = true 的定义:

  

Section 12.3 - 左侧表达式

     

<强>语法

LeftHandSideExpression

(您可以在上面的规范链接中查看 LeftHandSideExpression : NewExpression CallExpression NewExpression 的定义。

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

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

ReferenceError

使用括号,括号内优先于外部。这样,b1 = !(b2 = true); 被分配,因为它是b2,所以在括号内评估为true。接下来,它相当于:

true

如上所述,括号内部评估为b1 = !(true); true将与预期的b1相反,b2将为b2

如果您希望trueb1trueb2,请按以下方式重新构建语句:

false

这样,它与上面的完全相反,给出b2 = !(b1 = true); b1 = true

正如@Bergi在评论中提到的那样,b2 = false被分配了正确的操作数,在这种情况下为b1,而不是true。< / SUP>

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

答案 1 :(得分:27)

b1 = b2 = true;

相当于

b2 = true;
b1 = true;

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

node interactive console assignement return value example

当你尝试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
  4. 您可以通过添加括号使其按预期工作:

    b1 = !(b2 = true);
    

答案 2 :(得分:10)

表达式b1 = !b2 = true;从右到左进行评估

首先:!b2 = true

然后:b1 = <result of previous assignment>

!b2 = true没有逻辑意义,因而错误。

如果您写b1 = b2 = true;,则不会发出任何此类错误

答案 3 :(得分:3)

提到AST 分配给右值1)(2

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

&#13;
&#13;
var b1, b2;
b1 = !b2 = true;
&#13;
&#13;
&#13;

确认表达式永远不会被评估