当你尝试这样做时:
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†。根据规范:
第12.14.1节 - 赋值运算符 - 静态语义:早期错误
AssignmentExpression : LeftHandSideExpression = AssignmentExpression
- 如果
LeftHandSideExpression
既不是ObjectLiteral
也不是ArrayLiteral
且LeftHandSideExpression
的IsValidSimpleAssignmentTarget
为false,则会出现早期引用错误。
IsValidSimpleAssignmentTarget
的定义如下:
Section 12.14.3 - Assignment Operators - Static Semantics: IsValidSimpleAssignmentTarget
AssignmentExpression :
YieldExpression
ArrowFunction
LeftHandSideExpression = AssignmentExpression
LeftHandSideExpression AssignmentOperator AssignmentExpression
1. Return false.
现在回顾一下你的代码:
b1 = !b2 = true
。
b1 = !b2
是可以的,因为它是
LeftHandSideExpression = AssignmentExpression
,因此对
IsValidSimpleAssignmentTarget
返回true。问题出现在当我们检查
!b2 = true
时。如果我们查看
LeftHandSideExpression
的定义:
Section 12.3 - Left-Hand-Side Expressions
Syntax
LeftHandSideExpression :
NewExpression
CallExpression
您可以在上面的规范链接中查看NewExpression
和CallExpression
的定义。
您可以看到!b2 = true
不是一个有效的AssignmentExpression
,因为它不符合条件LeftHandSideExpression = AssignmentExpression
。这是因为!b2
不是一个有效的LeftHandSideExpression
,也不是一个ObjectLiteral
或ArrayLiteral
,因此IsValidSimpleAssignmentTarget
返回false,抛出ReferenceError
。请注意,错误是早期错误,这意味着它在执行任何代码之前就被抛出,如@Bergi's comment所述。
您可以通过以下任一方式来解决这个问题,具体取决于您想要的结果:
b1 = !(b2 = true)
带有括号时,括号内的优先级高于外部。这样,b2
被赋值,并且因为它是 true
,所以括号内被计算为 true
。接下来,它等同于:
b1 = !(true)
作为之前提到的,括号内的内容被评估为
true
。因此,
b1
将是与预期相反的
b2
的值,而
b2
将是
true
。
如果您希望
b1
为
true
,
b2
为
false
,请按以下方式重构语句:
b2 = !(b1 = true)
这样做与上述情况完全相反,会使
b1 = true
,而
b2 = false
。
‡正如评论中@Bergi所提到的,b1
在这种情况下被赋予了右操作数true
,而不是!b2
。
†虽然大多数浏览器目前不支持ECMAScript 6(2015)的所有功能,而是使用 ECMAScript 5.1(2011),但规范对于两个版本都是相同的。所有定义都是相同的,因此解释仍然有效。
var b1, b2; !b2 = true; b1 = b2;
这显然是错误的。你不能将表达式赋值给一个值... - Andrew Li