为什么在JavaScript中合法使用((foo)=“bar”)?

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

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

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

这似乎是违反直觉的,因为人们会认为括号至less会解除引用,并且在赋值时抛出无效的左手边

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

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

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

有没有我没有看到的东西?

这确实是有效的。 您可以将任何简单的分配目标放在括号中。

正确标识时, =操作的左侧部分是LeftHandSideExpression 。 这可以通过各种优先级( MemberExpressionMemberExpressionMemberExpression到一个PrimaryExpression ,这又可能是一个CoverParenthesizedExpressionAndArrowParameterList

   expression式 [In,?Yield]  

(实际上,当用目标PrimaryExpressionparsing时,它是一个ParenthesizedExpression )。

所以至less在语法上是有效的。 是否真正有效的JS语法是由另一个因素决定的:早期的错误静态语义。 这些基本上是散文或algorithm规则,在某些情况下会使某些产品扩展无效(语法错误)。 例如,这允许作者重新使用数组和对象初始化语法进行解构,但只能应用某些规则。 在分配expression式的早期错误中我们发现

如果LeftHandSideExpression既不是ObjectLiteral,也不是LeftHandSideExpressionArrayLiteralIsValidSimpleAssignmentTarget ,它是一个早期的Reference Error

我们也可以在赋值expression式的评估中看到这种区别,在赋值expression式中 ,简单的赋值目标被评估为可以赋值的引用,而不是像对象和数组文字那样得到解构模式的东西。

那么IsValidSimpleAssignmentTarget对LeftHandSideExpressions做了什么? 基本上它允许分配属性访问和不允许分配调用expression式。 它没有声明任何有关自己的IsValidSimpleAssignmentTarget规则的简单的PrimaryExpressions 。 它所做的只是通过CoveredParenthesizedExpression操作在括号之间提取expression式 ,然后再次检查IsValidSimpleAssignmentTarget。 简而言之: (…) = …… = …有效时有效。 它只会产生true标识符 (如你的例子)和属性。

根据@dlatikay的build议 ,在现有的预感之后,对CoveredParenthesizedExpression研究使人们对这里发生的事情有了更好的理解。

显然,在规范中找不到非terminal的原因是为了解释为什么(foo)可以被当作LeftHandExpression ,这是非常简单的。 我假设你理解parsing器是如何工作的,而且它们分两个阶段工作: LexingParsing

我从这个小小的研究中学到的东西是,构造(foo)在技​​术上并不是被传递给parsing器,因此引擎就像你想的那样。

考虑以下

 var foo = (((bar))); 

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

同样,即使从人类可读性的angular度来看,这里也是另一个有效的例子,因为括号只是说明了PEMDAS已经隐含的内容。

 (3 + ((4 * 5) / 2)) === 3 + 4 * 5 / 2 >> true 

一个关键的观察可以从这个松散的派生,给出了parsing器已经工作的理解。 (请记住,JavaScript仍然被parsing( 阅读:编译 ), 然后运行)所以在直接意义上,这些括号是“说明显而易见”

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

基本上,括号(除了函数参数)被折叠成它们包含符号的合适分组。 IANAL,但是按照非专业人士的说法,这意味着括号只能被解释为指导parsing器如何对其所读取的内容进行分组。 如果括号的上下文已经“按顺序”,并且因此不需要对发出的AST进行任何调整,则(机器)代码就好像完全不存在括号。

parsing器或多或less是懒惰的,假设parens是不相称的。 (在这种情况下,这是不正确的)

好的,这到底发生了什么?

根据12.2.1.5静态语义:IsValidSimpleAssignmentTarget在spec中,

PrimaryExpression:(CoverParenthesizedExpressionAndArrowParameterList)

  1. 假设expr是CoverParenthesizedExpressionAndArrowParameterList的CoveredParenthesizedExpression。
  2. 返回expr的IsValidSimpleAssignmentTarget。

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

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

TL; DR

这是笏。

想多一点见解?

看看@ Bergi的答案 。

Interesting Posts