How new.target Detects Constructor Invocations
The new.target meta-property in JavaScript provides a
reliable mechanism to detect whether a function or class constructor was
invoked with the new operator. Introduced in ECMAScript
2015 (ES6), it evaluates to a reference to the constructor function or
class that was instantiated, or resolves to undefined if
the function was called through standard invocation. This allows
developers to enforce constructor usage, build abstract classes, and
inspect inheritance chains dynamically during object creation.
How new.target Works
Unlike standard variables or properties, new.target is a
meta-property formed by the keyword new, a dot, and the
identifier target. It is available within all function
bodies and class constructors.
When a function executes, the JavaScript engine assigns a value to
new.target based on the call site:
- Constructed Calls: When a function or class is
invoked using
new(or viaReflect.construct),new.targetrefers to the function or class that was directly called. - Standard Calls: When a function is invoked normally
(e.g.,
fn(),fn.call(), orfn.apply()),new.targetevaluates toundefined.
Differentiating Constructor and Normal Function Calls
Before ES6, developers commonly relied on
this instanceof FunctionName to check if a function was
called with new. However, this pattern produces false
positives if a normal function call binds this to an
existing instance of the object using methods like .call()
or .apply().
Using new.target eliminates this ambiguity:
function DatabaseConnection() {
if (!new.target) {
throw new Error("DatabaseConnection must be instantiated with 'new'");
}
this.connected = true;
}
// Valid instantiation
const db = new DatabaseConnection(); // Works correctly
// Standard invocation throws error
DatabaseConnection(); // Error: DatabaseConnection must be instantiated with 'new'Alternatively, new.target can be used to make
constructors self-instantiating, automatically correcting calls that
omit new:
function User(name) {
if (!new.target) {
return new User(name);
}
this.name = name;
}
const user = User("Alice"); // Returns a new User instance transparentlyBehavior in Classes and Inheritance
In ES6 class hierarchies, new.target points to the
derived class constructor that was originally invoked, even inside the
constructor of a parent class. When super() is called,
new.target inside the superclass constructor retains the
value of the subclass constructor.
class Parent {
constructor() {
console.log(new.target.name);
}
}
class Child extends Parent {}
new Parent(); // Logs: "Parent"
new Child(); // Logs: "Child"Implementing Abstract Base Classes
Because new.target identifies the initial constructor in
the inheritance chain, it can be used to prevent direct instantiation of
abstract classes while allowing subclass instantiation:
class AbstractShape {
constructor() {
if (new.target === AbstractShape) {
throw new TypeError("Cannot instantiate abstract class directly.");
}
}
}
class Circle extends AbstractShape {}
const shape = new AbstractShape(); // Throws TypeError
const circle = new Circle(); // Instantiates successfullyBehavior in Arrow Functions
Arrow functions do not define their own new.target.
Instead, they inherit the new.target value from their
enclosing lexical context, mirroring how they resolve this
and arguments.
function Outer() {
const getTarget = () => new.target;
return getTarget();
}
console.log(Outer()); // undefined
console.log(new Outer()); // [Function: Outer]Attempting to invoke an arrow function directly with new
results in a runtime TypeError, as arrow functions lack an
internal [[Construct]] method.