Skip to main content

return inside a constructor

return in a constructor exists only to replace the instance being created with a different object. Without it the constructor implicitly returns this, and a returned primitive is simply ignored by the engine.

Theory

TL;DR

  • return in a constructor is optional: this is returned by default.
  • If you return an object, it replaces this and becomes the result of new.
  • If you return a primitive, it is ignored and the result is still this.
  • In a derived class super() must be called before any return, otherwise you get a ReferenceError.
  • The Singleton, Factory and Proxy patterns all build on this behaviour.

Quick example

javascript
class User { constructor(name) { this.name = name; } } const u = new User("Tim"); console.log(u); // User { name: "Tim" }

The normal behaviour of a constructor

When you write new User("Tim"), the engine does roughly this:

  1. Creates a new empty object {}.
  2. Sets the prototype of that object to User.prototype.
  3. Calls the constructor with this referring to that new object.
  4. Returns this from the constructor by default.

In other words, if you return nothing, return this happens implicitly.

Returning an object replaces this

If return hands back an object, it replaces the created instance:

javascript
class User { constructor(name) { this.name = name; return { custom: "another object" }; } } const u = new User("Tim"); console.log(u); // { custom: "another object" }

Note that this.name = name still ran, that object just went nowhere. The returned object has neither User.prototype nor the class methods, so u instanceof User is false.

Returning a primitive is ignored

Numbers, strings, booleans and other primitives cannot overwrite this:

javascript
class User { constructor(name) { this.name = name; return 42; } } const u = new User("Tim"); console.log(u); // User { name: "Tim" }

There is no error, the value is simply discarded. The same goes for null: it is a primitive, so it is ignored too, while return undefined changes nothing by definition.

Why this exists: Singleton and factories

The ability to return your own object is there so that a constructor can hand back something special. The classic example is a Singleton, one shared instance for every call:

javascript
class Database { constructor() { if (Database.instance) { return Database.instance; // always hands back the same object } this.connected = true; Database.instance = this; } } const a = new Database(); const b = new Database(); console.log(a === b); // true

return this is not needed here because the engine does it anyway, while return Database.instance substitutes the existing object for the new instance.

A constructor that returns different objects depending on its input works the same way:

javascript
class Example { constructor(x) { this.x = x; if (x > 10) return { value: "too large" }; } } console.log(new Example(5)); // Example { x: 5 } console.log(new Example(20)); // { value: "too large" }

return in derived classes

The rule is the same with and without extends: a returned object replaces this.

javascript
class A {} class B extends A { constructor() { super(); return { name: "replacement" }; } } const b = new B(); console.log(b); // { name: "replacement" }

But super() stays mandatory even when you return your own object:

javascript
class A {} class B extends A { constructor() { // error, even though there is a return return { test: 1 }; } } new B(); // ReferenceError: Must call super constructor before returning from derived constructor

All the rules in one place:

ScenarioWhat comes back
No returnthis, the created instance
return thisthe same instance
return { ... }that object instead of this
return 42 / "hi" / trueignored, this is returned
Derived class without super()an error: super() must precede return

Common mistakes

  • Returning an object literal "for convenience" and then being surprised that instanceof and the class methods are gone: the substituted object has no class prototype.
  • Expecting return 42 to make new User() a number. Primitives from a constructor are ignored with no warning at all.
  • An early return in a derived constructor before super(). That is a ReferenceError, not a quiet exit.
  • A Singleton written as return Database.instance with heavy initialisation happening before that check: the work is done again and then thrown away.
  • Confusion around null: it is a primitive, so new yields this, not null.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.