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

时间:2017-05-14 22:42:31

标签: javascript ecma

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

foo

有效,"bar"后来等于null而不是bar

这似乎违反直觉,因为人们会认为括号至少会取消引用(foo, bar) = 4 (true ? bar : foo) = 4 并在赋值中抛出无效的左侧

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

@RELATION points

@ATTRIBUTE x REAL
@ATTRIBUTE y REAL
@ATTRIBUTE id   STRING

@DATA
1,5,100 
2,6,200
3,5,300

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

有没有我没见过的东西?

2 个答案:

答案 0 :(得分:28)

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

正确识别后,=操作的左侧部分为LeftHandSideExpression。这可以通过各种优先级(NewExpressionMemberExpression)跟踪到PrimaryExpression,而后者可能是Cover­Parenthesized­Expression­And­Arrow­Parameter­List

( Expression[In, ?Yield] )

(实际上,在使用目标PrimaryExpression解析时,它是ParenthesizedExpression)。

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

  

如果 LeftHandSideExpression 既不是 ObjectLiteral 也不是 ArrayLiteral IsValidSimpleAssignmentTarget Reference Error LeftHandSideExpression 的>是false

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

那么IsValidSimpleAssignmentTarget LeftHandSideExpressions 做了什么?基本上,它允许分配属性访问,并禁止分配调用表达式。它没有说明具有own IsValidSimpleAssignmentTarget rule的普通 PrimaryExpressions 的任何内容。它只是通过Covered­Parenthesized­Expression operation在括号之间提取 Expression ,然后再次检查它的IsValidSimpleAssignmentTarget。简而言之:(…) = …… = …有效时有效。它仅为Identifiers(例如您的示例)和属性提供true

答案 1 :(得分:15)

根据@dlatikay's suggestion,按照现有的预感,对CoveredParenthesizedExpression的研究可以更好地了解这里发生的事情。

显然,在spec中找不到非终端的原因,解释为什么(foo)可以作为LeftHandExpression被接受,这简直太简单了。我假设您了解解析器的工作原理,并且它们分两个阶段运行: Lexing 解析

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

Consider the following

var foo = (((bar)));

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

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

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

在理解解析器已经如何工作的情况下,可以从中松散地得出一个关键的观察结果。 (记住,Javascript仍然被解析(read: compiled)和然后运行)所以从直接意义上说,这些括号是"说明显而易见的"

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

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

解析器或多或少是懒惰的,假设parens是不礼貌的。 (在这种边缘情况下,不是真的)

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

根据规范中的12.2.1.5 Static Semantics: IsValidSimpleAssignmentTarget

  

PrimaryExpression:(CoverParenthesizedExpressionAndArrowParameterList)

     
      
  1. 让expr成为CoverParenthesizedExpressionAndArrowParameterList的CoveredParenthesizedExpression。
  2.   
  3. 返回expr的IsValidSimpleAssignmentTarget。
  4.   

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

因此,在这种情况下,它不会将(foo)转换为CoveredParenthesizedExpression{ inner: "foo" },而只是将其转换为foo,这样可以保留Identifier这一事实从语法上来说,虽然不一定是词汇,但是有效。

TL; DR

It's wat.

想要更多洞察力?

查看@Bergi's answer