The specification does not explicitly mention it, but the syntax definitions include it with spaces as new . target
. This expression is known as NewTarget
, and you will encounter this term multiple times.
NewTarget is considered one of the meta properties and can be located in §12.3.8.
Its primary function is to fetch the current value of the [[NewTarget]] from the existing (non-arrow) function environment. This value is established when a function is invoked, similar to how the this
binding operates. As per §8.1.1.3 Function Environment Records:
If this Environment Record was created by the internal method [[Construct]], then [[NewTarget]] represents the [[Construct]] parameter newTarget
. Otherwise, it defaults to undefined
.
One significant aspect is that it allows us to determine whether a function was called as a constructor or not.
However, its true purpose lies beyond this. What exactly is it then? It plays a crucial role in ES6 classes by enabling inheritance from built-in objects rather than just serving as syntactic sugar. When a class
constructor is invoked using new X
, the this
value remains uninitialized - the object is not created until the constructor body is initiated. The creation occurs during the super()
call within the super constructor where internal slots are generated. The instance should inherit from the .prototype
of the original constructor, which is where newTarget comes into play. It holds the outermost constructor that received the new
call during super()
invocations. You can trace this flow in the specifications, but essentially, it is always the newTarget
passed into the OrdinaryCreateFromConstructor
procedure instead of the currently executing constructor - for example, in step 5 of §9.2.2 [[Construct]] for user-defined functions.
This explanation might be lengthy; perhaps an example would illustrate better:
class Parent {
constructor() {
// Implicit (derived from the `super` call)
// new.target = Child;
// Implicit (due to no extension in `Parent`):
// this = Object.create(new.target.prototype);
console.log(new.target) // Child!
}
}
class Child extends Parent {
constructor() {
// `this` is uninitialized (would throw error if accessed)
// Implicit (due to the `new` call):
// new.target = Child
super(); // this = Reflect.construct(Parent, [], new.target);
console.log(this);
}
}
new Child;