skip to content

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