JavaScript · Objects & Prototypes

The new Operator & Constructor Functions in JavaScript — How Object Creation Really Works

7 min read Updated 2026-06-18

Practice The new Operator & Constructors interview questions

What new really does

You write new Dog('Rex') thousands of times, but what does the new operator actually do? It's not magic — it's four well-defined steps, and knowing them demystifies constructors, the class keyword, and a whole category of bugs. When you understand new, you understand how JavaScript turns a plain function into an object factory.

A constructor function is just a normal function intended to be called with new. Convention capitalizes its name (Dog, not dog) to signal that intent.

The four steps of new

When you evaluate new Fn(args), the engine performs these steps:

  1. Create a brand-new empty object.
  2. Link that object's prototype to Fn.prototype.
  3. Call Fn with this bound to the new object, passing the arguments.
  4. Return the new object — unless the constructor explicitly returns its own object.

You can replicate it by hand to see there's nothing hidden:

function myNew(Fn, ...args) {
  const obj = Object.create(Fn.prototype)   // steps 1 + 2
  const result = Fn.apply(obj, args)        // step 3
  return (typeof result === 'object' && result !== null) ? result : obj  // step 4
}

function Dog(name) { this.name = name }
myNew(Dog, 'Rex')   // { name: 'Rex' } same as new Dog('Rex')

That Object.create(Fn.prototype) is exactly why instances inherit the constructor's prototype methods, and why instanceof works.

Building objects with a constructor

A constructor's job is to initialize the new object's own state via this. Shared methods go on the prototype so all instances share them.

function Circle(radius) {
  this.radius = radius        // per-instance data
}
Circle.prototype.area = function () {
  return Math.PI * this.radius ** 2   // shared behavior
}

const c = new Circle(5)
c.area()                 // 78.54...
c instanceof Circle      // true
Object.getPrototypeOf(c) === Circle.prototype   // true

Putting area on the prototype rather than inside the constructor means all circles share one function object — important when you create many instances.

The return-value rule

Step 4 has a subtlety: if the constructor explicitly returns an object, that object replaces the freshly-created this. If it returns a primitive (or nothing), the new object is used as normal.

function A() { this.x = 1; return { x: 99 } }
new A().x   // 99  — object return overrides `this` surprising

function B() { this.x = 1; return 42 }
new B().x   // 1   — primitive return ignored

This is why you almost never return from a constructor. The object-override behavior is occasionally used for factory tricks, but in normal code it's a source of confusion — let new return this implicitly.

The forgotten-new bug

The classic disaster: calling a constructor without new. Without it, there's no new object and this is whatever the call site provides — undefined in strict mode, or the global object in sloppy mode. Either way, your "instance" is wrong.

function User(name) { this.name = name }

const good = new User('Ada')   // proper instance
const bad  = User('Grace')     // in sloppy mode, sets global.name; returns undefined
bad        // undefined

In sloppy mode this silently pollutes the global object; in strict mode (and inside ES modules, which are always strict) it throws TypeError: Cannot set property 'name' of undefined, which at least fails loudly. This bug is exactly why class constructors throw if you call them without new.

Guarding against missing new

Before class, defensive constructors guarded themselves with new.target or an instanceof check:

function User(name) {
  if (!(this instanceof User)) return new User(name)  // auto-correct
  this.name = name
}
User('Ada').name   // 'Ada' — works with or without new

A cleaner modern tool is new.target, which is undefined when a function is called without new and the constructor reference when called with it.

function Account(balance) {
  if (new.target === undefined) {
    throw new Error('Account must be called with new')   // explicit guard
  }
  this.balance = balance
}

new.target and abstract constructors

new.target also enables "abstract" base constructors that must be subclassed, not instantiated directly.

function Shape() {
  if (new.target === Shape) {
    throw new Error('Shape is abstract — extend it instead')
  }
}
function Circle(r) { Shape.call(this); this.r = r }
Circle.prototype = Object.create(Shape.prototype)

new Circle(5)   // fine
new Shape()     // Error: Shape is abstract

Inside a subclass call, new.target is the subclass, so the abstract check only fires when someone instantiates the base directly.

The constructor property

Every function's prototype object has a constructor property pointing back to the function, so instances can discover what made them.

function Dog() {}
const d = new Dog()
d.constructor === Dog          // true
d.constructor.name             // 'Dog'

When you reassign Fn.prototype wholesale (common in manual inheritance), you wipe out this back-pointer and must restore it: Dog.prototype.constructor = Dog. Forgetting to do so breaks code that relies on instance.constructor.

Constructors vs factory functions

You don't have to use new. A factory function just builds and returns an object, sidestepping this and new entirely.

function createUser(name) {
  return {
    name,
    greet() { return `Hi, ${name}` }   // closure over name, no `this` needed
  }
}
const u = createUser('Ada')   // no `new`, no this-binding pitfalls

Factories avoid the forgotten-new bug and the confusion around this, but each returned object gets its own copy of every method (no shared prototype), which costs memory at scale. Constructors (and class) trade a little ceremony for prototype-based method sharing. Choose factories for small counts and simplicity; constructors/classes for many instances.

How class relates

The class keyword is sugar over everything above. class Dog {} creates a constructor function Dog, puts its methods on Dog.prototype, wires up extends via the same prototype linking, and adds the guard that throws when called without new. There's no new runtime mechanism — just nicer syntax over constructors and prototypes.

Key takeaways

  • new Fn() does four things: create an object, link it to Fn.prototype, call Fn with this bound to it, and return it (unless the constructor returns its own object).
  • Initialize instance state on this; put shared methods on Fn.prototype.
  • A constructor returning an object overrides the new instance; returning a primitive is ignored.
  • Calling a constructor without new is a classic bug — undefined/global this; guard with new.target.
  • new.target distinguishes new calls and enables abstract base constructors.
  • Factory functions avoid this/new entirely at the cost of per-instance methods; class is just sugar over constructors and prototypes.

Understanding new turns constructors and classes from incantations into a clear, four-step process you could implement yourself — which is exactly the level of understanding that separates confident JavaScript developers from the rest.

Practice tests are coming soon

Get notified when interactive mock interviews and quizzes launch.