skip to content

What is `new.target` inside a JavaScript function, and how do you use it to guard a constructor against being called without `new`?

level: middleimportance: should knowfreq 44%

answer

  1. a meta-property, not an object lookup
  2. undefined means it was a plain call
  3. one comparison, two possible guards
  4. in a subclass chain it names the caller's class
  5. arrows borrow it from the enclosing function

basics

~20 s

new.target is a meta-property readable inside a function body: it is undefined when the function was called normally and holds the constructor that was invoked when it was constructed. Guards test it to throw, or to re-invoke construction automatically.

solid answer

~50 s

`new.target` lets a function ask how it was invoked. Inside an ordinary call it is `undefined`; inside a construction it is the constructor that `new` was applied to. That single bit is enough to write a guard. The strict version rejects the mistake: `if (!new.target) throw new TypeError('Dog must be called with new')`, so a forgotten keyword fails at the call site instead of leaking state. The forgiving version self-corrects: `if (!new.target) return new Dog(name)`, which lets callers write `Dog('Rex')` or `new Dog('Rex')` interchangeably. There is a third use inside class hierarchies: because `new.target` is the constructor the caller actually named, a base constructor can compare `new.target === Shape` and refuse direct instantiation while still allowing subclasses through — the closest JavaScript gets to an abstract base. Arrow functions have no `new.target` of their own; they see the enclosing function's.

code

javascript · 18 lines
javascript
function Dog(name) {
  if (!new.target) throw new TypeError('Dog must be called with new');
  this.name = name;
}

new Dog('Rex').name;   // 'Rex'
try { Dog('Rex'); } catch (e) { console.log(e.message); }
// 'Dog must be called with new'

class Shape {
  constructor() {
    if (new.target === Shape) throw new TypeError('Shape is abstract');
  }
}
class Circle extends Shape {}
console.log(new Circle() instanceof Shape);   // true
try { new Shape(); } catch (e) { console.log(e.message); }
// 'Shape is abstract'

go deeper

for a junior

Recall the single fact that makes the guard work: reading new.target inside a function gives undefined for a normal call and the constructor when the call used new.

for a middle

Explain both guard shapes — throw, or return a construction of the same function — and say what the value is inside a class hierarchy, where it names the constructor the caller actually invoked.

for a senior

Justify choosing a guard over the older receiver-inspection test, and defend where you would put hardening: a shipped library boundary is worth guarding, an internal helper usually is not.

for a principal

Own the API stance. Decide whether your codebase makes construction mandatory, optional, or impossible, and weigh the abstract-base idiom against a factory that simply never exposes a constructible base at all.

## What `new.target` is `new.target` is a *meta-property* — a fixed piece of syntax, not a normal property lookup on an object called `new`. You can read it inside a function body (including a class constructor) and it tells you which constructor, if any, drove the current invocation. It is only meaningful inside a function or constructor body; there is nothing sensible for it to mean at the top level of a script. The values you will see: - **`undefined`** — the function was invoked as a plain call: `Dog('Rex')`, `dog.method()`, `fn.call(obj)`, `arr.map(fn)`. - **the constructor** — the function was constructed: inside `new Dog('Rex')`, `new.target` is `Dog`. - **a subclass** — in a class hierarchy, the constructor the caller actually named. Constructing `new Circle()` where `class Circle extends Shape` makes `new.target` equal `Circle` inside *both* constructors, because the value propagates down the `super()` chain rather than being reset to the class you are lexically inside. ```js function probe() { return new.target; } probe(); // undefined new probe(); // [Function: probe] ``` ## Guard 1: reject the mistake The defensive form makes a forgotten keyword impossible to miss: ```js function Dog(name) { if (!new.target) { throw new TypeError("Dog must be called with new"); } this.name = name; } ``` The payoff is locality. Without the guard, a forgotten `new` in sloppy code writes a property onto the global object and hands back `undefined`, and the program fails somewhere else entirely; with it, the stack trace points at the caller who made the mistake. This is what a `class` constructor does for you automatically — the guard is how you get the same protection out of a plain function. ## Guard 2: construct anyway The forgiving form makes the keyword optional, an idiom older APIs used so that both call styles work: ```js function Dog(name) { if (!new.target) return new Dog(name); this.name = name; } Dog('Rex') instanceof Dog; // true new Dog('Rex') instanceof Dog; // true ``` It is convenient, and it costs you something: the API no longer teaches callers which style it wants, and the auto-construct branch has to forward every argument, which is easy to get wrong when the signature grows. Prefer the throwing guard in new code unless you have a specific reason to accept both. ## Guard 3: an abstract base Because `new.target` names the constructor the caller invoked rather than the one you are inside, a base can detect direct instantiation: ```js class Shape { constructor() { if (new.target === Shape) { throw new TypeError("Shape is abstract; construct a subclass"); } } } class Circle extends Shape {} new Circle(); // fine — new.target is Circle new Shape(); // TypeError ``` JavaScript has no `abstract` keyword, so this comparison is the standard way to express the constraint. The same trick supports registries and diagnostics: a base can log or record `new.target.name` to know which concrete type was built. ## Arrow functions An arrow function has no `new.target` binding of its own, exactly as it has no `this` of its own. Reading `new.target` inside an arrow gives whatever the enclosing ordinary function's value is. That is usually what you want — a helper arrow defined inside a constructor still reports the constructor — but it means an arrow cannot be used as the guarded function itself. ## Why it beats the older heuristics Before `new.target`, the common test was whether the receiver looked like an instance of the constructor. That test answers a different question — it inspects the prototype chain of whatever `this` happens to be — so it is fooled in both directions: an ordinary call whose receiver was deliberately set to an existing instance passes the check even though nothing was constructed, and a legitimately constructed object whose prototype was reassigned can fail it. `new.target` asks the runtime directly how the function was invoked, which is the question you actually meant. ## Practical notes A guard costs one comparison per construction, which is noise next to the object allocation, so there is no real performance argument against it. Put it on the first line so it runs before any side effect. And be honest about scope: the guard protects against a forgotten keyword, not against a caller who deliberately invokes the internal construct behaviour through other means.

  • Why is a `new.target` guard better than testing whether `this` is an instance of the constructor?
    They answer different questions. The instance test inspects whatever receiver was passed, so a plain call made with an existing instance as the receiver slips through, and a legitimate instance whose prototype was replaced fails it. `new.target` reports how the runtime actually invoked the function, which is the fact you wanted, and it needs no assumption about the prototype chain.
  • What does `new.target` evaluate to inside a base-class constructor when a subclass is constructed?
    The subclass the caller named, not the base you are lexically inside. Constructing `new Circle()` where `Circle extends Shape` leaves `new.target === Circle` inside `Shape`'s constructor too, because the value propagates through the `super()` call. That is precisely what makes the abstract-base comparison `new.target === Shape` work.
  • Can you put the guard in an arrow function?
    No. An arrow has no `new.target` binding of its own; it reads the enclosing ordinary function's value, and an arrow cannot be constructed in the first place. Write the guarded function as a `function` declaration or a class constructor, and use arrows only for helpers defined inside it.

saying these in an interview costs you the question

  • Says new.target holds the newly created object
  • Claims new.target is the constructor's name as a string
  • Believes new.target is always the base class in a subclass chain
  • Thinks an arrow function gets its own new.target
  • Treats new.target as a property you could add or overwrite

context