为什么在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
。 这可以通过各种优先级( MemberExpression
, MemberExpression
) MemberExpression
到一个PrimaryExpression
,这又可能是一个CoverParenthesizedExpressionAndArrowParameterList
:
( expression式 [In,?Yield] )
(实际上,当用目标PrimaryExpression
parsing时,它是一个ParenthesizedExpression
)。
所以至less在语法上是有效的。 是否真正有效的JS语法是由另一个因素决定的:早期的错误静态语义。 这些基本上是散文或algorithm规则,在某些情况下会使某些产品扩展无效(语法错误)。 例如,这允许作者重新使用数组和对象初始化语法进行解构,但只能应用某些规则。 在分配expression式的早期错误中我们发现
如果LeftHandSideExpression既不是ObjectLiteral,也不是LeftHandSideExpression的ArrayLiteral和IsValidSimpleAssignmentTarget ,它是一个早期的
Reference Error
。
我们也可以在赋值expression式的评估中看到这种区别,在赋值expression式中 ,简单的赋值目标被评估为可以赋值的引用,而不是像对象和数组文字那样得到解构模式的东西。
那么IsValidSimpleAssignmentTarget对LeftHandSideExpressions做了什么? 基本上它允许分配属性访问和不允许分配调用expression式。 它没有声明任何有关自己的IsValidSimpleAssignmentTarget规则的简单的PrimaryExpressions 。 它所做的只是通过CoveredParenthesizedExpression操作在括号之间提取expression式 ,然后再次检查IsValidSimpleAssignmentTarget。 简而言之: (…) = …
在… = …
有效时有效。 它只会产生true
的标识符 (如你的例子)和属性。
根据@dlatikay的build议 ,在现有的预感之后,对CoveredParenthesizedExpression
研究使人们对这里发生的事情有了更好的理解。
显然,在规范中找不到非terminal的原因是为了解释为什么(foo)
可以被当作LeftHandExpression
,这是非常简单的。 我假设你理解parsing器是如何工作的,而且它们分两个阶段工作: Lexing和Parsing 。
我从这个小小的研究中学到的东西是,构造(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)
- 假设expr是CoverParenthesizedExpressionAndArrowParameterList的CoveredParenthesizedExpression。
- 返回expr的IsValidSimpleAssignmentTarget。
IE如果期望primaryExpression
返回括号内的任何内容并使用它。
因此,在这种情况下,它不会将(foo)
转换为CoveredParenthesizedExpression{ inner: "foo" }
,它会将其简单地转换为foo
,从而保留了它是一个Identifier
的事实,因此在语法上也是如此, 。
TL; DR
这是笏。
想多一点见解?
看看@ Bergi的答案 。