Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does "new Date().toString()" work given Javascript operator precedence?

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?

like image 247
Alex Lopatin Avatar asked Jul 11 '13 07:07

Alex Lopatin


2 Answers

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 
like image 144
georg Avatar answered Oct 08 '22 05:10

georg


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.

like image 26
Aadit M Shah Avatar answered Oct 08 '22 05:10

Aadit M Shah