为什么我可以执行以下操作:
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;
然而,当我尝试以下操作时,我收到ReferenceError: invalid assignment left-hand side
?
var b1, b2;
b1 = !b2 = true;
document.write(b1," ", b2);
&#13;
很明显,我无法做到这一点,但我无法找到解释为何我无法解释。错误的MDN developer guide表示:
某处有意外的任务。例如,这可能是由于赋值运算符和比较运算符的不匹配造成的。单个&#34; =&#34; sign为变量赋值,&#34; ==&#34;或&#34; ===&#34;运营商比较一个值。
所有赋值运算符都经过验证,因此为什么不能将它组合成单个运算/链式赋值?
答案 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
。
如果您希望true
为b1
而true
为b2
,请按以下方式重新构建语句:
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 DevTools,NodeJS,jsc)。请参阅spec details in Andrew's answer。
当你尝试b1 = !b2 = true;
时,等价物毫无意义:
(!b2) = true; // true = true; causes the error.
b1 = b2; // never evaluated.
这是因为 !
在=
分配运算符上需要precedence,如(!b2)
中的括号所示。
订单:
b2
是undefined
,因为它尚未初始化。 !b2 === true
为!undefined === true
,!b2
变为true
,true = true
。您可以通过添加括号使其按预期工作:
b1 = !(b2 = true);
答案 2 :(得分:10)
表达式b1 = !b2 = true;
从右到左进行评估
首先:!b2 = true
然后:b1 = <result of previous assignment>
!b2 = true
没有逻辑意义,因而错误。
如果您写b1 = b2 = true;
,则不会发出任何此类错误
答案 3 :(得分:3)