The new.target
meta-property lets you detect whether a function or constructor was called using the undefined
.
The new.target
meta-property lets you detect whether a function or constructor was called using the undefined
.
function Foo() {
if (!new.target) {
throw new TypeError("calling Foo constructor without new is invalid");
}
}
try {
Foo();
} catch (e) {
console.log(e);
/ Expected output: TypeError: calling Foo constructor without new is invalid
}
new.target
new.target
is guaranteed to be a constructable function value or undefined
.
new
was called upon, which may be a subclass of the current constructor, because subclasses transitively call the superclass's constructor through super()
.extends
, in which case new.target
may refer to the subclass.Reflect.construct()
, then new.target
refers to the value passed as newTarget
(which defaults to target
).undefined
.The new.target
syntax consists of the keyword new
, a dot, and the identifier target
. Because new
is 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.
In normal function calls (as opposed to constructor function calls), new.target
is new
as a constructor.
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"
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.
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"
Before this
, and letting the base constructor mutate it.
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, 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()
.
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.
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
Specification |
---|
ECMAScript® 2026 Language Specification # sec-built-in-function-objects |