为什么“new Date()。toString()”在给定Javascript运算符优先级的情况下工作?

时间:2013-07-11 07:47:43

标签: javascript parsing operator-precedence

MDN states Javscript中有两个共享优先级最高的运算符:

  • 左关联成员运算符:foo.bar
  • 右关联新运算符:new Foo()

我通常明确地将两者分开:(new Date()).toString()
但我经常看到它们两者合并:new Date().toString()

根据this answer,第二种方式起作用的原因在于,当两个运算符具有相同的优先级时,它是第二个运算符的关联性。在这种情况下,成员运算符是左关联的,这意味着首先评估new Date()

但是,如果是这种情况,那么为什么new Date.toString()会失败?毕竟,对于new Datenew Date()just syntactic sugar。上面的论点说它应该有用,但显然不行。

我错过了什么?

2 个答案:

答案 0 :(得分:22)

syntax

MemberExpression :
    PrimaryExpression
    FunctionExpression
    MemberExpression [ Expression ]
    MemberExpression . IdentifierName
    new MemberExpression Arguments

new foo().bar无法解析为new (foo().bar),因为foo().bar不是MemberExpression。此外,出于同样的原因,new foo()无法解析为new (foo())。相反,new foo.bar 解析为new (foo.bar)因为foo.bar是有效的MemberExpression(解释(new foo).bar是不可能的,因为语法是贪婪的。)< / p>

也就是说,优先规则是:dot beats new,new beats call(parens)。

.  -> new -> ()

此外,直接查看语法揭开了将new Foo变为new Foo()的句法糖。这只是NewExpression←new NewExpression←new PrimaryExpression:

NewExpression :
    MemberExpression
    new NewExpression

答案 1 :(得分:6)

我是写“Disambiguation of expressions with neighboring operators of different associativity and same precedence”的问题和答案的人,当我写这篇文章时,我脑子里没有JavaScript。

我正在考虑的语言是Haskell,它是一种函数式编程语言。这些语言的操作员只是功能而且更容易推理。但是我以一种不假设任何编程语言的方式写了我的答案。

另一方面,JavaScript是一种传统的编程语言,JavaScript中的表达式基于精细的解析规则消除歧义,这些规则与Haskell使用的解析规则非常不同。

特别是JavaScript解析规则似乎很贪婪。例如,举个例子:

new Date().toString()

此处Date之后的函数调用从成员运算符屏蔽Date。因此,贪婪的new仍然只能在Date而不是Date().toString上运行。因此我们有:

((new Date()).toString)()

在第二个例子中,我们有:

new Date.toString()

此处Date之后没有函数调用来保护它免受成员操作符的影响。因此,贪婪的new对表达式Date.toString进行操作。因此我们有:

(new (Date.toString))()

@ thg435的回答支持了这一主张。关键是我正在讨论一个与JavaScript解析器实现的完全不同的正式系统。我正在讨论的正式系统将运算符和操作数都视为一等值。