为什么在 JavaScript 运算符优先级的情况下 "new Date().toString()" 能够正常工作?

38

MDN指出,在Javscript中有两个具有最高优先级的运算符:

  • 左结合成员运算符:foo.bar
  • 右结合的新运算符:new Foo()

我通常会明确地将这两者分开:(new Date()).toString()
但我经常看到它们组合在一起:new Date().toString()

根据this answer,第二种方式有效的原因是当两个运算符具有相等的优先级时,第二个运算符的结合性很重要。在这种情况下,成员运算符是左结合的,这意味着new Date()会先被计算。

然而,如果是这样的话,为什么new Date.toString()会失败呢?毕竟,new Date只是new Date()语法糖。上述论点说它应该起作用,但显然并不起作用。
我错过了什么?

1
因为语法糖不包括实例化和成员访问。这取决于编译器。类似的情况是:VB.NET有Dim x As New ...,但我不认为我们可以做Dim x As New Date().ToString()。我们可以做Dim x As String = new Date().ToString() - ps2goat
1
() 运算符将在 . 访问其属性之前执行构造函数。 - Broxzier
@ps2goat 你说得对,但根据下面的答案,这是由指定的语法而不是编译器的怪癖造成的。 - Alex Lopatin
“new” 的语法规则很复杂,容易导致错误。我更喜欢的解决方案是将“new”作为静态方法,并在内部调用“new this(…)”。这样,创建新实例的语法看起来就像任何其他方法调用一样,我不必再思考两次。例如,我可以写:MyDate..new().toString()。 - Panu Logic
2个回答

27

语法

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是不可能的,因为语法是贪婪的)。

也就是说,优先级规则是:点的优先级高于new,new的优先级高于调用(括号)。

.  -> new -> ()

此外,直接查看语法可以揭示将new Foo转化为new Foo()的语法糖,它只是NewExpression ← new NewExpression ← new PrimaryExpression:

NewExpression :
    MemberExpression
    new NewExpression

1
根据这个定义,foo()不是一个FunctionExpression吗?那么foo().bar不就是一个MemberExpression了吗? - riv

8

我是写了“具有不同结合性和相同优先级的相邻运算符的消歧义”问题和答案的人,当时我并没有考虑 JavaScript。

我考虑的语言是 Haskell,这是一种函数式编程语言。在这样的语言中,运算符只是函数,更容易理解。但是,我用一种不假设任何编程语言的方式来写我的答案。

另一方面,JavaScript 是一种传统的编程语言,JavaScript 中的表达式是根据复杂的解析规则进行消歧义的,这些规则与 Haskell 使用的解析规则非常不同。

特别是 JavaScript 的解析规则似乎很贪心。例如,看看你的第一个例子:

new Date().toString()

在这里,Date函数调用后屏蔽了成员操作符,因此new仍然只能操作Date而不是Date().toString。因此我们有:

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

在第二个例子中,我们有:
new Date.toString()

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

(new (Date.toString))()

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接