Baseline Widely available
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
.
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
}
Syntax Value
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()
.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.Reflect.construct()
, then new.target
refers to the value passed as newTarget
(which defaults to target
).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.new.target
is undefined
.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.
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.
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.
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.
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()
.
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
Specifications Browser compatibility See also
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4