MDN states that there are two operators in Javscript that share the highest precedence:
foo.bar
new Foo()
I usually explicitly separate the two: (new Date()).toString()
But I frequently see both of them combined: new Date().toString()
According to this answer, the reason the second way works is that it's the second operator's associativity that matters when both operators have equal precedence. In this case, the member operator is left associative which means new Date()
is evaluated first.
However, if that's the case, then why does new Date.toString()
fail? After all, new Date
is just syntactic sugar for new Date()
. The above argument says it should work, but it obviously doesn't.
What am I missing?
The syntax is
MemberExpression : PrimaryExpression FunctionExpression MemberExpression [ Expression ] MemberExpression . IdentifierName new MemberExpression Arguments
new foo().bar
cannot be parsed as new (foo().bar)
because foo().bar
is not a MemberExpression. Moreover, new foo()
cannot be parsed as new (foo())
, for the same reason. Conversely, new foo.bar
is parsed as new (foo.bar)
because foo.bar
is a valid MemberExpression (an interpretation (new foo).bar
is impossible because the grammar is greedy).
That is, the precedence rule is: dot beats new, new beats call (parens).
. -> new -> ()
Furthermore, looking directly at the grammar demystifies the syntactic sugar that turns new Foo
into new Foo()
. It's simply NewExpression ← new NewExpression ← new PrimaryExpression:
NewExpression : MemberExpression new NewExpression
I'm the guy who wrote both the question and the answer of "Disambiguation of expressions with neighboring operators of different associativity and same precedence", and when I wrote that I didn't have JavaScript in my mind.
The language I was considering was Haskell, which is a functional programming language. Operators in such languages are simply functions and are much easier to reason about. However I wrote my answer in a manner which didn't assume any programming language.
On the other hand JavaScript is a traditional programming language and expressions in JavaScript are disambiguated based on elaborate parsing rules which are very different from the parsing rules employed by Haskell.
In particular JavaScript parsing rules seem to be greedy. For example take your first example:
new Date().toString()
Here the function call after Date
shields Date
from the member operator. Hence new
, being greedy, can still only operate on Date
instead of Date().toString
. Hence we have:
((new Date()).toString)()
In the second example we have:
new Date.toString()
Here there's no function call after Date
to shield it from the member operator. Hence new
, being greedy, operates on the expression Date.toString
. Hence we have:
(new (Date.toString))()
@thg435's answer backs up this claim. The point is that I was discussing a formal system which is totally different from the one implemented by JavaScript parsers. The formal system I was discussing treats operators and operands both as first class values.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With