MDN states Javscript中有两个共享优先级最高的运算符:
foo.bar
new Foo()
我通常明确地将两者分开:(new Date()).toString()
但我经常看到它们两者合并:new Date().toString()
根据this answer,第二种方式起作用的原因在于,当两个运算符具有相同的优先级时,它是第二个运算符的关联性。在这种情况下,成员运算符是左关联的,这意味着首先评估new Date()
。
但是,如果是这种情况,那么为什么new Date.toString()
会失败?毕竟,对于new Date
,new Date()
为just syntactic sugar。上面的论点说它应该有用,但显然不行。
我错过了什么?
答案 0 :(得分:22)
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解析器实现的完全不同的正式系统。我正在讨论的正式系统将运算符和操作数都视为一等值。