根据@dlatikay 的建议,根据现有的预感,研究CoveredParenthesizedExpression
对这里发生的事情有了更好的理解。
显然,在规范中找不到非终结符的原因,解释为什么(foo)
可以接受为 a 的原因LeftHandExpression
非常简单。我假设您了解解析器的工作原理,并且它们在两个不同的阶段运行:Lexing和Parsing。
我从这个小小的研究切线中学到的是,从(foo)
技术上讲,该构造并未交付给解析器,因此正如您所想的那样,引擎并未交付。
var foo = (((bar)));
众所周知,这样的事情是完全合法的。为什么?好吧,您可以在视觉上忽略括号,而该语句仍然非常有意义。
同样,这是另一个有效的示例,即使从人类可读性的角度来看也是如此,因为括号仅说明了PEMDAS已经隐含的内容。
(3 + ((4 * 5) / 2)) === 3 + 4 * 5 / 2
>> true
考虑到解析器已经如何工作,可以从中大致得出一个关键的观察结果。(请记住,Javascript 仍在被解析(阅读:已编译)然后运行)所以在直接意义上,这些括号是“陈述显而易见的”。
说了这么多,到底发生了什么?
基本上,括号(函数参数除外)被折叠成它们包含的符号的适当分组。IANAL 但是,用外行的话来说,这意味着括号仅被解释为指导解析器如何对它读取的内容进行分组。如果括号的上下文已经“有序”,因此不需要对发出的AST进行任何调整,则发出(机器)代码,就好像这些括号根本不存在一样。
假设括号是无礼的,解析器或多或少是懒惰的。(在这种边缘情况下,这是不正确的)
好的,这到底发生在哪里?
根据规范中的12.2.1.5 Static Semantics: IsValidSimpleAssignmentTarget,
PrimaryExpression:(CoverParenthesizedExpressionAndArrowParameterList)
- 令 expr 为 CoverParenthesizedExpressionAndArrowParameterList 的 CoveredParenthesizedExpression。
- 返回 expr 的 IsValidSimpleAssignmentTarget。
IE 如果期望primaryExpression
返回括号内的任何内容并使用它。
因此,在这种情况下,它不会转换(foo)
为CoveredParenthesizedExpression{ inner: "foo" }
,而是将其简单地转换为foo
,保留了它是 an 的事实,Identifier
因此在语法上,但不一定在词法上有效。
TL;博士
是笏。
想要更多的见解?
查看@Bergi 的答案。