47

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

var foo = null;
(foo) = "bar";

是有效的,foo随后等于而"bar"不是null

这似乎违反直觉,因为有人会认为括号至少会取消引用bar并在赋值中抛出Invalid left-hand side`

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

(foo, bar) = 4
(true ? bar : foo) = 4

根据有关 LeftHandExpressions 的 ECMA-262(据我所知),没有有效的非终结符会导致括号被接受。

有什么我没看到的吗?

4

2 回答 2

29
于 2017-05-15T00:12:53.633 回答
15

根据@dlatikay 的建议,根据现有的预感,研究CoveredParenthesizedExpression对这里发生的事情有了更好的理解。

显然,在规范中找不到非终结符的原因,解释为什么(foo)可以接受为 a 的原因LeftHandExpression非常简单。我假设您了解解析器的工作原理,并且它们在两个不同的阶段运行:LexingParsing

我从这个小小的研究切线中学到的是,从(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)

  1. 令 expr 为 CoverParenthesizedExpressionAndArrowParameterList 的 CoveredParenthesizedExpression。
  2. 返回 expr 的 IsValidSimpleAssignmentTarget。

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

因此,在这种情况下,它不会转换(foo)CoveredParenthesizedExpression{ inner: "foo" },而是将其简单地转换为foo,保留了它是 an 的事实,Identifier因此在语法上,但不一定在词法上有效。

TL;博士

是笏。

想要更多的见解?

查看@Bergi 的答案

于 2017-05-14T23:41:08.093 回答