2

Consider a class defined as:

class Foo { static bar() { return this; } }

I can construct an instance with new (Foo.bar())().

But new Foo.bar()() results in an Uncaught TypeError: Foo.bar is not a constructor.

Why are those extra parentheses necessary?

Dave Peck
  • 1,342
  • 1
  • 17
  • 24
  • 5
    `new Foo.bar()()` is identical to `(new Foo.bar())()`. – ASDFGerte Jan 07 '20 at 19:53
  • Because `bar` is not a constructor. – Keith Jan 07 '20 at 19:53
  • Order of operations. First example, call foo.bar(), whatever it returns make a `new` instance of that, then execute it. Second example, make a `new` foo.bar, execute it and assuming it returns a function execute that as well. Quite different. – James Jan 07 '20 at 19:53
  • @ASDFGerte why? – Dave Peck Jan 07 '20 at 19:58
  • (My assumption is this comes down to some operator precedence thing -- aka new with arguments is higher than function call but lower than grouping?) – Dave Peck Jan 07 '20 at 19:59
  • 2
    It's somewhat an edge-case, as function calls and new are in the same group when it comes to [operator precedence](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence), and new is somewhat odd in the spec, but it's how the parsing works, you can check e.g. with https://astexplorer.net/ – ASDFGerte Jan 07 '20 at 20:00
  • @ASDFGerte Sorta what I figured. Thanks. And thanks for the link to astexplorer.net -- hadn't seen that before. – Dave Peck Jan 07 '20 at 20:05
  • Generally, when writing code, and precedence is not immediately clear (uncommon, tricky situation), add explicit braces. Even if you don't need braces, but it's one of these edge cases, then people who are not completely submerged in the material may read the code, and get confused. Of course, it is always beneficial to know the precedence, but often, adding a pair of braces too many helps readability. Another note: the error finally stems from class methods not being constructors. They don't have [[Construct]], and cannot be used with `new`. – ASDFGerte Jan 07 '20 at 20:30
  • @ASDFGerte Given i can't find a suitable duplicate, could you please post your comments as an answer? – Bergi Jan 07 '20 at 23:31

2 Answers2

2

new Foo.bar()() is identical to (new Foo.bar())().

Looking at the MDN page for operator precedence, new and a function call are in the same group. In the spec, the handling of new can be somewhat confusing. However, new will be done first.

A nice tool to check, how a specific snippet is being parsed, is https://astexplorer.net/ - it allows you to look at the AST which is the result of parsing.

The error message you get then comes from Foo.bar being a class method, which don't have [[Construct]], and cannot be used with new.

As a tip: when it is slightly unclear, how an expression will be parsed, then adding explicit braces often improves readability, even when they may not be necessary.

ASDFGerte
  • 4,695
  • 6
  • 16
  • 33
-1
  • First, you need to know what is IIFE(immediately invoked function expression). IIFE is as name suggested you can call a function immediately after declared. But JS parser should see whole as one expression.

Ex.

(function(){console.log("foo")}())

!function(){console.log("foo")}()

+function(){console.log("foo")}()

All these forces parser to see function(){console.log("foo")}() side of it as complete expression so it can call immediately.

  • Second, how new (Foo.bar())() works.

When you use new (Foo.bar())() in first parentheses returns this which is Foo itself. Because bar method is returning Foo. After second parentheses() calls it(Foo) and then new is creates a new instance of Foo. Basically you are writing new Foo() with using IIEF.

  • Third why new Foo.bar()() throws error.

When JS parser sees the expression new Foo.bar() it is immediately try creates an instance if Foo.bar and Foo.baris not a constructor. After that JS throws that error.

But if parser sees () it could throw another error that Uncaught SyntaxError: Unexpected token ')'. Because JS can not understand the what is left from that line, ().

Difference between

new Foo.bar()() and new (Foo.bar())()

same as

function(){console.log("foo")}() and (function(){console.log("foo")})()

it throws different error and stop parsing, because Foo.bar is not a constructor to call.

I hope it helps.

Some links to understand. What does the exclamation mark do before the function?

Samed
  • 136
  • 1
  • 14