MDN states that there are two operators in Javscript that share the highest precedence:
- The left-associative member operator:
foo.bar
- The right-associative new operator:
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?
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:
Here the function call after
Date
shieldsDate
from the member operator. Hencenew
, being greedy, can still only operate onDate
instead ofDate().toString
. Hence we have:In the second example we have:
Here there's no function call after
Date
to shield it from the member operator. Hencenew
, being greedy, operates on the expressionDate.toString
. Hence we have:@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.
The syntax is
new foo().bar
cannot be parsed asnew (foo().bar)
becausefoo().bar
is not a MemberExpression. Moreover,new foo()
cannot be parsed asnew (foo())
, for the same reason. Conversely,new foo.bar
is parsed asnew (foo.bar)
becausefoo.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).
Furthermore, looking directly at the grammar demystifies the syntactic sugar that turns
new Foo
intonew Foo()
. It's simply NewExpression ← new NewExpression ← new PrimaryExpression: