Zze*_*Zze 37 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);Run Code Online (Sandbox Code Playgroud)
然而,当我尝试以下操作时,我收到了一个ReferenceError: invalid assignment left-hand side?
var b1, b2;
b1 = !b2 = true;
document.write(b1," ", b2);Run Code Online (Sandbox Code Playgroud)
很明显,我不能这样做,但我无法找到解释为什么我不能.错误的MDN开发人员指南指出:
某处有意外的任务.例如,这可能是由于赋值运算符和比较运算符的不匹配造成的.虽然单个"="符号为变量赋值,但"=="或"==="运算符会比较一个值.
所有赋值运算符都经过验证,因此为什么不能将它组合成单个运算/链式赋值?
Li3*_*357 71
当你尝试这样做:
var b1, b2;
b1 = !b2 = true;
document.write(b1, " ", b2);
Run Code Online (Sandbox Code Playgroud)
因为它们功能相同‡你基本上是这样做的:
var b1, b2;
!b2 = true;
b1 = true; //just the value of b2, not b2 itself
document.write(b1, " ", b2);
Run Code Online (Sandbox Code Playgroud)
在该行中!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语言规范†.根据规格:
第12.14.1节 -赋值运算符 - 静态语义:早期错误
AssignmentExpression : LeftHandSideExpression = AssignmentExpression
- 这是一个早期参考错误如果
LeftHandSideExpression既不是ObjectLiteral也不是ArrayLiteral和IsValidSimpleAssignmentTarget的LeftHandSideExpression是假的.
在哪里IsValidSimpleAssignmentTarget:
第12.14.3节 -赋值运算符 - 静态语义:IsValidSimpleAssignmentTarget
Run Code Online (Sandbox Code Playgroud)AssignmentExpression : YieldExpression ArrowFunction LeftHandSideExpression = AssignmentExpression LeftHandSideExpression AssignmentOperator AssignmentExpression1.返回假.
现在回顾一下你的代码:b1 = !b2 = true.b1 = !b2很好,因为它是LeftHandSideExpression = AssignmentExpression,因此返回真实IsValidSimpleAssignmentTarget.我们检查时会出现问题!b2 = true.如果我们看一下定义LeftHandSideExpression:
第12.3节 -左侧表达式
句法
Run Code Online (Sandbox Code Playgroud)LeftHandSideExpression : NewExpression CallExpression
(可以查看的定义NewExpression和CallExpression以上说明书中的链接)
您可以看到它!b2 = true不是有效的AssignmentExpression,因为它不符合标准LeftHandSideExpression = AssignmentExpression.这是因为!b2不是有效的LeftHandSideExpression,也不是ObjectLiteral也不是ArrayLiteral,因此IsValidSimpleAssignmentTarget返回false,抛出ReferenceError.请注意,错误是一个早期错误,这意味着它会在任何代码执行之前抛出,如@ Bergi的评论中所述.
您可以通过执行以下任一操作来解决此问题,具体取决于您所需的结果:
b1 = !(b2 = true);
Run Code Online (Sandbox Code Playgroud)
使用括号时,括号内优先于外部.这样,b2分配,并且因为它true,在括号内评估true.接下来,它相当于:
b1 = !(true);
Run Code Online (Sandbox Code Playgroud)
如上所述,在括号内部进行评估true.b1将与b2预期相反,b2并将成为true.
如果你想b1成为true和b2将来false,重组这样的声明:
b2 = !(b1 = true);
Run Code Online (Sandbox Code Playgroud)
这样,它与上面的完全相反,给出b1 = true,和b2 = false.
‡正如@Bergi在评论中提到的那样,b1分配了正确的操作数,true在这种情况下,不是!b2.
†虽然大多数浏览器目前不支持ECMAScript 6(2015)的所有功能,而是使用ECMAScript 5.1(2011),但两种版本的规范相同.所有定义都相同,因此解释仍然有效.
Emi*_*ron 27
b1 = b2 = true;
Run Code Online (Sandbox Code Playgroud)
相当于
b2 = true;
b1 = true;
Run Code Online (Sandbox Code Playgroud)
一个赋值返回正确的操作.这很容易交互式控制台(如内看到Chrome的DevTools,的NodeJS,JSC).请参阅Andrew的答案中的规格细节.
当你尝试时b1 = !b2 = true;,等价物毫无意义:
(!b2) = true; // true = true; causes the error.
b1 = b2; // never evaluated.
Run Code Online (Sandbox Code Playgroud)
这是因为!需要优先于=赋值运算符这表现在括号中(!b2).
订单如下:
b2是undefined因为它尚未初始化.!b2 === true作为!undefined === true,所以!b2变得true,true = true.您可以通过添加括号使其按预期工作:
b1 = !(b2 = true);
Run Code Online (Sandbox Code Playgroud)
mri*_*rid 10
表达式b1 = !b2 = true;从右到左进行评估
第一: !b2 = true
然后 : b1 = <result of previous assignment>
!b2 = true 没有逻辑意义,因此错误.
如果你写b1 = b2 = true;,它不会给出任何这样的错误
| 归档时间: |
|
| 查看次数: |
4447 次 |
| 最近记录: |