From the specification:
11.2.2 The new Operator # Ⓣ Ⓡ Ⓖ
The production NewExpression :
new
NewExpression is evaluated as follows:
- Let ref be the result of evaluating NewExpression.
- Let constructor be
GetValue
(ref).- If
Type
(constructor) is not Object, throw a TypeError exception.- If constructor does not implement the [[Construct]] internal method, throw a TypeError exception.
- Return the result of calling the [[Construct]] internal method on constructor, providing no arguments (that is, an empty list of arguments).
The production MemberExpression :
new
MemberExpression Arguments
is evaluated as follows:
- Let ref be the result of evaluating MemberExpression.
- Let constructor be
GetValue
(ref).- Let argList be the result of evaluating Arguments, producing an internal list of argument values (11.2.4).
- If
Type
(constructor) is not Object, throw a TypeError exception.- If constructor does not implement the [[Construct]] internal method, throw a TypeError exception.
- Return the result of calling the [[Construct]] internal method on constructor, providing the list argList as the argument values.
In either case, all steps are correctly followed:
var objPrototype = Object.create(this.prototype); // 1-4 1-5
var instance = this.apply(objPrototype, arguments); // 5 6
The point of interest is 2.
The specification for [[construct]]
states:
When the [[Construct]] internal method for a Function object F is
called with a possibly empty list of arguments, the following steps
are taken:
- Let obj be a newly created native ECMAScript object.
. . .
- Let result be the result of calling the [[Call]] internal property of F, providing obj as the this value and providing the argument list
passed into [[Construct]] as args.- If
Type
(result) is Object then return result.- Return obj.
typeof obj
returns "object"
for null
, while null
is not an object. However, since null
is a falsy value, your code also works as intended:
return (typeof instance === 'object' && instance ) || objPrototype;