skip to content

Constructor Return-Value Rules

A constructor that returns an object hands that object back instead of the freshly built `this`, while a returned primitive is silently ignored. The rule shows up in trick questions and in real singleton and caching tricks.

part ofJavaScriptoverview, primer and where to startread it →
on this pageshow

questions

4

In a JavaScript constructor function invoked with `new`, what happens if the function body explicitly returns an object, and what happens if it returns a primitive such as 42 or null?

level: middleimportance: must knowfreq 62%

answer

  1. depends on what the body returns
  2. objects and primitives behave differently
  3. primitive returns are silently discarded
  4. an object return replaces this
  5. null counts as a primitive here

basics

~20 s

An object returned from a constructor replaces the freshly created this that new would otherwise hand back. A returned primitive - number, string, boolean, symbol, bigint, null or undefined - is ignored, and new still returns this.

solid answer

~50 s

`new` builds a fresh object, links it to the constructor's `prototype`, runs the body with `this` bound to it, and then inspects the body's return value. If that value is an **object** (including a function or an array), `new` hands back *that* object and silently discards the `this` it just built. If the return value is anything else - a number, string, boolean, `null`, `undefined`, or no `return` at all - it is ignored and `new` evaluates to `this`. `null` trips people up because `typeof null === 'object'`, but the spec's Type(null) is Null, not Object, so it falls in the ignored bucket. The body still runs completely either way; the assignments to `this` happen and are simply thrown away when an object overrides it. Note the override object is *not* linked to the constructor's prototype, so `instanceof` and prototype methods break unless you build it that way deliberately.

code

javascript · 19 lines
javascript
function Widget(name) {
  this.name = name;
  return { name: 'replacement' };
}

function Gadget(name) {
  this.name = name;
  return 42;
}

function Gizmo(name) {
  this.name = name;
  return null;
}

console.log(new Widget('original').name); // 'replacement'
console.log(new Gadget('original').name); // 'original'
console.log(new Gizmo('original').name);  // 'original'
console.log(new Widget('x') instanceof Widget); // false

go deeper

for a junior

Recall the split: returning an object from a constructor makes new give back that object, and returning a number, string or boolean changes nothing. Say it plainly with a one-line example.

for a middle

Explain the mechanics behind the split - new checks whether the returned value's type is Object, so null is ignored while an array or a boxed new Number(1) overrides. Note the body still runs and its this is simply discarded.

for a senior

Show why this bites in production: the override object is not linked to Fn.prototype, so instanceof and prototype methods quietly stop working, and anything the constructor registered points at the abandoned object. Be ready to name a case where you would use the override deliberately.

for a principal

Frame it as an API-contract question: a constructor that hands back something other than a fresh instance breaks the promise new makes to every caller. Argue when that deception is worth it versus exposing a plain factory function that never lies about what it returns.

## The mechanism Calling a function with `new` invokes its `[[Construct]]` internal method, which does roughly four things: create a fresh ordinary object whose `[[Prototype]]` is the constructor's `.prototype` property, call the function body with `this` bound to that object, look at the value the body returned, and decide what the whole `new` expression evaluates to. That last step is the rule this question is about. The specification says: if the body's return value is an **Object**, return that value; otherwise return the `this` that was created. There is no merging, no unwrapping, no error. ```js function Widget(name) { this.name = name; return { name: 'replacement' }; } new Widget('original').name; // 'replacement' ``` ## "Object" means the spec's Object type An object here is anything whose specification type is Object: a plain object literal, an array, a function, a `Date`, a `Map`, a `Promise`, a boxed `new Number(1)`. All of them override `this`. Everything else is a primitive and is discarded: numbers, strings, booleans, symbols, bigints, `undefined`, and - the classic trap - `null`. ```js function Gizmo(name) { this.name = name; return null; } new Gizmo('original').name; // 'original' ``` `typeof null` famously reports `'object'`, a bug preserved from the first JavaScript engine for compatibility, but the language's own Type() operation classifies `null` as Null. `new` follows the spec, not `typeof`, so `return null` behaves exactly like `return 42` or falling off the end of the function. A subtle relative: `return new Number(42)` *does* override `this`, because that is a wrapper object, not a primitive. Interviewers occasionally use that pair to check whether you understand the boundary rather than memorised "numbers are ignored". ## The body still runs When an object return overrides `this`, the discarded object was still created and still mutated. Every statement executed, every side effect landed, and any reference you leaked out of the constructor (`registry.push(this)`) still points at the abandoned object. Only the *result of the `new` expression* changes. The abandoned object is then unreachable and collected. This matters for a real bug shape: a constructor that registers `this` somewhere and then returns a wrapper leaves the registry holding the wrong object. ## The returned object is not an instance The prototype link is established on the object `new` created, not on whatever you return. A plain object literal inherits from `Object.prototype`, so: ```js function Widget() { return {}; } new Widget() instanceof Widget; // false new Widget().someProtoMethod; // undefined ``` If you want the override *and* a working instance, build the replacement from the right prototype yourself - `Object.create(Widget.prototype)` - or return an object that was itself produced by that constructor. This is why the override rule is a foot-gun in ordinary code and a deliberate tool only in narrow cases. ## Where the rule is used on purpose Three recurring uses: - **Instance caching / single-instance objects.** The constructor stashes its first instance and returns it on later calls, so every `new` yields the same object. - **Returning a different shape than the class implies.** A constructor that returns a `Proxy` wrapping the real instance, so property access can be intercepted while callers keep writing `new Thing()`. - **Defensive factories.** Older code wrote `function Foo(){ if (!(this instanceof Foo)) return new Foo(); ... }` so the function works with or without `new` - the recursive call returns an object, which overrides whatever `this` happened to be. ## Classes obey the same rule `class` constructors are constructors, so a base class behaves identically: return an object and it replaces the instance; return a primitive and it is ignored. Derived classes (those written with `extends`) are the one place the rules tighten - returning a non-`undefined` primitive there is an error rather than a no-op. ## How it is probed The question almost always arrives as a "what does this print" puzzle with two or three constructors side by side - one returning an object, one returning a number, one returning `null`. The strong answer names the object-vs-primitive split, correctly classifies `null` as a primitive for this purpose, and adds that the override object does not inherit from `Fn.prototype`. The weak answer guesses that returns from constructors are always ignored, or that the returned value is merged with `this`.

  • Does the object you return from a constructor still inherit from that constructor's prototype?
    No. `new` links the object *it* created to `Fn.prototype`; your replacement keeps whatever prototype it already had. A plain literal inherits from `Object.prototype`, so `instanceof` fails and prototype methods are missing. If you need both the override and a real instance, build it with `Object.create(Fn.prototype)` or return an object that constructor previously produced.
  • Do the assignments to `this` still happen when the constructor returns a different object?
    Yes. The body runs to completion: the fresh object is created, every property assignment lands on it, and every side effect happens. Only the value of the `new` expression changes - the built object is discarded and becomes garbage. That is why a constructor that registers `this` in a list and then returns a wrapper leaves the list holding the wrong object.
  • What does `return new Number(42)` do, given that plain `return 42` is ignored?
    It overrides `this`. `new Number(42)` produces a wrapper *object*, whose specification type is Object, so the override rule applies and `new` hands back that wrapper. The distinction is primitive versus object, not "numeric versus not", and this pair is a common way to check that you know which side of the line each value sits on.

saying these in an interview costs you the question

  • Says a constructor cannot return anything at all
  • Thinks `return 42` makes `new` evaluate to 42
  • Believes `return null` yields null because typeof null is object
  • Assumes the returned object is merged with this
  • Assumes the returned object still inherits from Fn.prototype

context

open as a page

Why does `new (() => {})()` throw a TypeError in JavaScript, and which other function forms are non-constructible in the same way?

level: juniorimportance: should knowfreq 45%

basics

~20 s

Arrow functions are callable but not constructible: they have no [[Construct]] internal method and no prototype property, so new throws a TypeError. Concise object and class methods, getters and setters, generator functions and async functions are non-constructible too.

open as a page

Inside a JavaScript class declared with `extends`, what values may its constructor return, and how do those rules differ from a class that extends nothing?

level: middleimportance: should knowfreq 32%

basics

~20 s

A derived constructor may return only an object or undefined; returning any other value, including null, throws a TypeError. A base constructor is looser - it ignores every primitive return, including null, and simply hands back its instance.

open as a page

A JavaScript constructor function stores its first instance on a property of itself and returns that stored object on every later call, so `new Config()` always yields the same object. How does that work, and what problems does it cause in production code?

level: seniorimportance: nice to knowfreq 28%

basics

~20 s

It exploits the rule that an object returned from a constructor replaces the freshly built this, so every call after the first hands back the cached instance. The costs are silently ignored constructor arguments, hidden global state that tests cannot reset, and subclasses that receive the base instance instead of their own.

open as a page