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:

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 transparently

Behavior 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 successfully

Behavior 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.