为什么`(foo)="bar"在JavaScript中合法?

TER*_*Mtm 46 javascript ecma

在Node.js的REPL中(也在SpiderMonkey中测试)序列

var foo = null;
(foo) = "bar";
Run Code Online (Sandbox Code Playgroud)

是有效的,foo随后等于"bar"相反null.

这似乎违反直觉,因为人们会认为括号至少会取消引用bar在赋值中抛出无效的左侧.

可以理解的是,当你做任何有趣的事情时,它确实以上述方式失败了.

(foo, bar) = 4
(true ? bar : foo) = 4
Run Code Online (Sandbox Code Playgroud)

根据ECMA-262关于LeftHandExpressions(据我可以解释),没有有效的非终端会导致括号被接受.

有什么我没看到的吗?

Ber*_*rgi 28

它确实有效.您可以在括号中包装任何简单的赋值目标.

操作的左侧部分=LeftHandSideExpression您正确识别的部分.这可以通过各种优先级(NewExpression,MemberExpression)跟踪到a PrimaryExpression,而a 又可能是Cover­Parenthesized­Expression­And­Arrow­Parameter­List:

( Expression[In, ?Yield] )

(实际上,当用目标解析时PrimaryExpression,它是a ParenthesizedExpression).

所以它至少在语法上是有效的.它是否真正有效的JS语法是由另一个因素决定的:早期错误静态语义.这些基本上是散文或算法规则,在某些情况下使某些生产扩展无效(语法错误).例如,这允许作者重用数组和对象初始化语法进行解构,但仅应用某些规则.在我们发现的赋值表达式早期错误中

这是一个早期的Reference Error,如果LeftHandSideExpression既不是ObjectLiteral也不是ArrayLiteralIsValidSimpleAssignmentTargetLeftHandSideExpressionfalse.

我们还可以在赋值表达式求值中看到这种区别,其中简单的赋值目标被计算为可以赋值的引用,而不是获取像对象和数组文字这样的解构模式.

那么IsValidSimpleAssignmentTargetLeftHandSideExpressions做了什么?基本上,它允许分配属性访问,并禁止分配调用表达式.它没有说明具有自己的IsValidSimpleAssignmentTarget规则的普通PrimaryExpressions的任何内容.它所做的是提取表达式通过括号之间CoveredParenthesizedExpression操作,然后再次检查该IsValidSimpleAssignmentTarget.简而言之:有效时有效.它只会为标识符(如您的示例)和属性生成.(…) = …… = …true

  • @JIXiang我不知道 - 尤其是因为它不允许所有模式(例如对象解构,它会有用).也许这只是在定义箭头函数参数封面语法时发生的失误.你应该在es-discuss询问. (2认同)

TER*_*Mtm 15

根据@ dlatikay的建议,在现有的预感之后,通过研究CoveredParenthesizedExpression可以更好地理解这里发生的事情.

显然,在规范中找不到非终端的原因,解释为什么(foo)可以接受为一个LeftHandExpression,这简直令人惊讶.我假设您了解解析器的工作原理,并且它们分两个阶段运行:LexingParsing.

我从这个小小的研究中得到的结论是,这个结构(foo)在技​​术上并没有被提供给解析器,因此你可能会想到引擎.

考虑以下

var foo = (((bar)));
Run Code Online (Sandbox Code Playgroud)

众所周知,这样的事情是完全合法的.为什么?那么你在视觉上可以忽略括号,而声明仍然是完美的意义.

类似地,这是另一个有效的例子,即使从人类可读性的角度来看,因为括号只能说明PEMDAS已经隐含的内容.

(3 + ((4 * 5) / 2)) === 3 + 4 * 5 / 2
>> true
Run Code Online (Sandbox Code Playgroud)

考虑到解析器已经如何工作,可以从中松散地得出一个关键的观察结果.(记住,Javascript仍然被解析(读取:编译)然后运行)所以从直接意义上说,这些括号是"陈述明显的".

所有这一切,究竟是怎么回事?

基本上,括号(函数参数除外)将折叠为其包含符号的正确分组.IANAL,但是,用非专业人的术语来说,这意味着括号仅被解释为指导解析器如何对其读取的内容进行分组.如果括号的上下文已经"按顺序",因此不需要对发出的AST进行任何调整,则发出(机器)代码,就好像这些括号根本不存在一样.

假设parens是无关紧要的,解析器或多或少是懒惰的.(在这个边缘情况下,不是真的)

好的,这究竟发生在哪里?

根据12.2.1.5静态语义:规范中的IsValidSimpleAssignmentTarget,

PrimaryExpression:(CoverParenthesizedExpressionAndArrowParameterList)

  1. 设expr为CoverParenthesizedExpressionAndArrowParameterList的CoveredParenthesizedExpression.
  2. 返回expr的IsValidSimpleAssignmentTarget.

IE如果期望primaryExpression返回括号内的任何内容并使用它.

因此,在这种情况下,它不会转换(foo)CoveredParenthesizedExpression{ inner: "foo" },它只是将其转换为foo保留它是一个事实,Identifier从而在语法上,而不一定是词法上有效的事实.

TL; DR

这是扫管笏.

想要更多洞察力?

查看@ Bergi的答案.