new.target

The new.target meta-property lets you detect whether a function or constructor was called using the new operator. In constructors and functions invoked using the new operator, new.target returns a reference to the constructor or function that new was called upon. In normal function calls, new.target is undefined.

Try it

Syntax

js

new.target

Value

new.target is guaranteed to be a constructable function value or undefined.

  • In class constructors, it refers to the class that new was called upon, which may be a subclass of the current constructor, because subclasses transitively call the superclass's constructor through super().
  • In ordinary functions, if the function is constructed directly with new, new.target refers to the function itself. If the function is called without new, new.target is undefined. Functions can be used as the base class for extends, in which case new.target may refer to the subclass.
  • If a constructor (class or function) is called via Reflect.construct(), then new.target refers to the value passed as newTarget (which defaults to target).
  • In arrow functions, new.target is inherited from the surrounding scope. If the arrow function is not defined within another class or function which has a new.target binding, then a syntax error is thrown.
  • In static initialization blocks, new.target is undefined.

Description

The new.target syntax consists of the keyword new, a dot, and the identifier target. Because new is a reserved word, not an identifier, this is not a property accessor, but a special expression syntax.

The new.target meta-property is available in all function/class bodies; using new.target outside of functions or classes is a syntax error.

Examples

new.target in function calls

In normal function calls (as opposed to constructor function calls), new.target is undefined. This lets you detect whether a function was called with new as a constructor.

js

function Foo() {
  if (!new.target) {
    throw new Error("Foo() must be called with new");
  }
  console.log("Foo instantiated with new");
}

new Foo(); // Logs "Foo instantiated with new"
Foo(); // Throws "Foo() must be called with new"

new.target in constructors

In class constructors, new.target refers to the constructor that was directly invoked by new. This is also the case if the constructor is in a parent class and was delegated from a child constructor. new.target points to the class that new was called upon. For example, when b was initialized using new B(), the name of B was printed; and similarly, in case of a, the name of class A was printed.

js

class A {
  constructor() {
    console.log(new.target.name);
  }
}

class B extends A {
  constructor() {
    super();
  }
}

const a = new A(); // Logs "A"
const b = new B(); // Logs "B"

new.target using Reflect.construct()

Before Reflect.construct() or classes, it was common to implement inheritance by passing the value of this, and letting the base constructor mutate it.

js

function Base() {
  this.name = "Base";
}

function Extended() {
  // Only way to make the Base() constructor work on the existing
  // `this` value instead of a new object that `new` creates.
  Base.call(this);
  this.otherProperty = "Extended";
}

Object.setPrototypeOf(Extended.prototype, Base.prototype);
Object.setPrototypeOf(Extended, Base);

console.log(new Extended()); // Extended { name: 'Base', otherProperty: 'Extended' }

However, call() and apply() actually call the function instead of constructing it, so new.target has value undefined. This means that if Base() checks whether it's constructed with new, an error will be thrown, or it may behave in other unexpected ways. For example, you can't extend Map this way, because the Map() constructor cannot be called without new.

All built-in constructors directly construct the entire prototype chain of the new instance by reading new.target.prototype. So to make sure that (1) Base is constructed with new, and (2) new.target points to the subclass instead of Base itself, we need to use Reflect.construct().

js

function BetterMap(entries) {
  // Call the base class constructor, but setting `new.target` to the subclass,
  // so that the instance created has the correct prototype chain.
  return Reflect.construct(Map, [entries], BetterMap);
}

BetterMap.prototype.upsert = function (key, actions) {
  if (this.has(key)) {
    this.set(key, actions.update(this.get(key)));
  } else {
    this.set(key, actions.insert());
  }
};

Object.setPrototypeOf(BetterMap.prototype, Map.prototype);
Object.setPrototypeOf(BetterMap, Map);

const map = new BetterMap([["a", 1]]);
map.upsert("a", {
  update: (value) => value + 1,
  insert: () => 1,
});
console.log(map.get("a")); // 2

Note: In fact, due to the lack of Reflect.construct(), it is not possible to properly subclass built-ins (like Error subclassing) when transpiling to pre-ES6 code.

However, if you are writing ES6 code, prefer using classes and extends instead, as it's more readable and less error-prone.

js

class BetterMap extends Map {
  // The constructor is omitted because it's just the default one

  upsert(key, actions) {
    if (this.has(key)) {
      this.set(key, actions.update(this.get(key)));
    } else {
      this.set(key, actions.insert());
    }
  }
}

const map = new BetterMap([["a", 1]]);
map.upsert("a", {
  update: (value) => value + 1,
  insert: () => 1,
});
console.log(map.get("a")); // 2

Specifications

Specification
ECMAScript Language Specification
# sec-built-in-function-objects

Browser compatibility

BCD tables only load in the browser

See also