jsguides

Reflect.construct()

Reflect.construct(target, argumentsList[, newTarget])

Description

Reflect.construct() invokes a target constructor with a list of arguments already in array form, optionally swapping in a separate newTarget for new.target and the prototype chain. It is the reflective counterpart to the new operator, introduced in ES2015 alongside the Reflect namespace object and Proxy.

You reach for Reflect.construct() when you have constructor arguments as an array or array-like rather than as a spread, when you want to call a constructor you only have as a value, or when you need to run one constructor’s logic while exposing a different function as new.target. The third argument is the part plain new cannot do on its own.

Syntax

Reflect.construct(target, argumentsList)
Reflect.construct(target, argumentsList, newTarget)

Parameters

NameTypeRequiredDescription
targetconstructoryesThe constructor to invoke through the [[Construct]] internal slot. Must be a constructor; class declarations and function declarations work, methods and arrow functions do not.
argumentsListarray-like objectyesAn array or array-like object whose elements are spread as positional arguments to target. Must be an object; null and undefined throw TypeError.
newTargetconstructornoDefaults to target. Provides the value of new.target inside the constructor and the prototype used for the new instance (newTarget.prototype). Must be a constructor when supplied.

Return value

A new instance of target (or newTarget when supplied), initialized by target running as a constructor with argumentsList. If the constructor returns an object explicitly, that returned object is used instead of the auto-created instance, even when newTarget is passed.

Examples

Construct from an argument array

The basic case: you already have the arguments as an array and you do not want to spread them.

function Point(x, y) {
  this.x = x;
  this.y = y;
}

const args = [3, 4];
const p = Reflect.construct(Point, args);

console.log(p);
// Point { x: 3, y: 4 }
console.log(p instanceof Point);
// true

This is equivalent to new Point(3, 4). The spread would work too, but when the argument list is dynamic (an arguments object, a config-derived array, a list pulled from somewhere else) Reflect.construct() keeps the call site compact and the array intact for inspection.

Build a built-in instance

Reflect.construct() works on built-ins the same way new does. Date is target, no newTarget is passed, so new.target === Date inside the constructor.

const d = Reflect.construct(Date, [1776, 6, 4]);

console.log(d instanceof Date);
// true
console.log(d.getFullYear());
// 1776

Separate target from new.target

The third argument is what Reflect.construct() adds over plain new. target runs to initialize the object. newTarget is what shows up as new.target inside target, and newTarget.prototype becomes the prototype of the result.

function OneClass() {
  console.log(`new.target is ${new.target.name}`);
}

function OtherClass() {
  console.log(`new.target is ${new.target.name}`);
}

const obj1 = Reflect.construct(OneClass, []);
// new.target is OneClass
console.log(obj1 instanceof OneClass);
// true

const obj2 = Reflect.construct(OneClass, [], OtherClass);
// new.target is OtherClass
console.log(obj2 instanceof OtherClass);
// true
console.log(obj2 instanceof OneClass);
// false

OneClass runs in both calls. What changes is which function “owns” the prototype of the resulting object and which name the constructor sees through new.target.

Subclass a built-in

This is the production use case for the third argument. You want a Map (or Array, Error, etc.) built by Map’s own constructor, but you want the resulting instance to carry your subclass’s prototype so it picks up your extra methods.

class MyMap extends Map {
  firstKey() {
    return this.keys().next().value;
  }
}

const m = Reflect.construct(Map, [['a', 1], ['b', 2]], MyMap);

console.log(m instanceof MyMap);
// true
console.log(m instanceof Map);
// true
console.log(m.firstKey());
// "a"

new Map(...entries) cannot produce an instance whose prototype chain includes MyMap.prototype. Reflect.construct() is how frameworks build subclass-aware collections, error wrappers, and similar wrappers around built-ins without writing a class extends for every variant.

Constructor that returns an object

If the constructor returns an object, that object is the result, and the prototype chain follows the returned object rather than newTarget.prototype.

function OneClass() {
  return { name: 'one' };
}

function OtherClass() {
  return { name: 'other' };
}

const obj = Reflect.construct(OneClass, [], OtherClass);

console.log(obj.name);
// "one"
console.log(obj instanceof OneClass);
// false
console.log(obj instanceof OtherClass);
// false

The rule is the same as for new: an explicit object return overrides the auto-constructed instance, and the newTarget linkage is bypassed.

Forward from a Proxy construct trap

A construct trap should hand off to Reflect.construct() so the newTarget supplied by the caller is propagated.

function MyClass(x) {
  this.x = x;
}

const P = new Proxy(MyClass, {
  construct(target, args, newTarget) {
    return Reflect.construct(target, args, newTarget);
  }
});

const instance = new P(42);

console.log(instance.x);
// 42
console.log(instance instanceof MyClass);
// true

Replacing the body with new target(...args) would drop newTarget and break subclasses that rely on new.target matching the proxied function. This is the same reason handler.apply reaches for Reflect.apply() instead of calling through directly.

Notes

  • Reflect.construct() throws TypeError if target is not a constructor. Methods, arrow functions, and async functions used as target will not work.
  • Reflect.construct() throws TypeError if argumentsList is null or undefined (or otherwise not an object). Pass [] for “no arguments”.
  • Reflect.construct() throws TypeError if newTarget is supplied and is not a constructor.
  • The result’s prototype is newTarget.prototype when the constructor does not return an object. If newTarget.prototype is not an object, the new instance inherits from Object.prototype instead.
  • Returning an object from the constructor overrides the auto-constructed instance, including the prototype linkage that newTarget would have set.
  • Reflect.construct() was added in ES2015 and is available in every current browser engine and in Node.js 6+. The core-js polyfill or the es-shims/reflect.construct npm package cover older runtimes.

See also