new F(...args) evaluates F and the arguments, throws a TypeError if F is not a constructor, then calls F.[[Construct]](args, newTarget). For an ordinary function (a base constructor) that does four things:
- Create an ordinary object.
- Link it: its
[[Prototype]]isnewTarget.prototype. If that property isn't an object, the intrinsicObject.prototypeis used instead. - Call
Fwiththisbound to the new object andnew.targetset to the constructor. - Return a value: if
Freturns an object (including a function or array), that is the result ofnewand the object from step 1 is discarded. Otherwise, the new object is the result and the return value is ignored.
The return rule is the one interviewers probe: return 42 is ignored, return {} replaces the instance. In a derived class constructor, the rules are stricter: returning anything but an object or undefined throws a TypeError, and this isn't created until super() returns.
Only constructors have [[Construct]]: ordinary functions and classes, bound versions of them, and some built-ins like Date or Map. Arrow functions, shorthand methods, async functions and Math.max do not, so new on them throws TypeError: ... is not a constructor. A class constructor does the reverse and throws if called without new.
Inside a function, new.target is the constructor new was applied to, or undefined for a plain call. It lets a function detect how it was called, and it is how Reflect.construct(F, args, NewTarget) and subclassing choose which prototype to link.