The new Keyword in JavaScript: What Actually Happens

Problem
You've written constructor functions. You've used new. But if someone asked you to explain what JavaScript does internally when new runs — step by step — could you?
Most developers can't, and that gap causes real bugs: unexpected undefined properties, broken prototype chains, and confusion when constructor functions behave differently with and without new.
This post fixes that.
What new Does
When you write:
const dog = new Animal("Rex");
JavaScript executes four steps internally, in this order:
Step 1 — Create a fresh object: An empty object {} is created.
Step 2 — Link the prototype: The new object's [[Prototype]] is set to Animal.prototype. This is what makes instanceof work and enables method lookup via the prototype chain.
Step 3 — Run the constructor: The constructor function (Animal) is called with this bound to the new object. Any property assignments inside the constructor land on this object.
Step 4 — Return the object: Unless the constructor explicitly returns a different object, the new object is returned automatically.
Here's that flow visually:---
Constructor Functions
A constructor function is just a regular function — the convention is to capitalize it. The magic isn't in the function itself, it's in calling it with new.
function Animal(name, sound) {
this.name = name;
this.sound = sound;
}
// Method lives on the prototype — shared across all instances
Animal.prototype.speak = function () {
return `\({this.name} says \){this.sound}`;
};
const dog = new Animal("Rex", "woof");
const cat = new Animal("Miso", "meow");
console.log(dog.speak()); // Rex says woof
console.log(cat.speak()); // Miso says meow
Why put speak on the prototype, not inside the constructor?
If you defined speak as this.speak = function() {...} inside the constructor, every instance would get its own copy of that function — wasted memory. By putting it on Animal.prototype, all instances share one copy and look it up via the prototype chain.
The Object Creation Process, Step by Step
Here's a manual recreation of what new does — without using new:
function manualNew(Constructor, ...args) {
// Step 1: create empty object
const obj = {};
// Step 2: link prototype
Object.setPrototypeOf(obj, Constructor.prototype);
// Step 3: run constructor with `this` = new object
const result = Constructor.apply(obj, args);
// Step 4: return the new object (or constructor's return if it's an object)
return result instanceof Object ? result : obj;
}
function Vehicle(make, model) {
this.make = make;
this.model = model;
}
Vehicle.prototype.describe = function () {
return `\({this.make} \){this.model}`;
};
const car = manualNew(Vehicle, "Toyota", "Camry");
console.log(car.describe()); // Toyota Camry
console.log(car instanceof Vehicle); // true
Property lookup order:
Check the instance itself (
d.name→ found)Check
Dog.prototype(d.breed→ found)Check
Object.prototype(.hasOwnProperty,.toString, etc.)null→undefined
Here's what the prototype chain looks like:---
Instances from Constructors
Multiple instances from the same constructor share the prototype but own their own data:
function Counter(start = 0) {
this.count = start;
}
Counter.prototype.increment = function () {
this.count += 1;
return this;
};
Counter.prototype.value = function () {
return this.count;
};
const a = new Counter(0);
const b = new Counter(10);
a.increment().increment();
b.increment();
console.log(a.value()); // 2
console.log(b.value()); // 11
// They share the prototype methods but not the data
console.log(a.increment === b.increment); // true (same function reference)
console.log(a.count === b.count); // false (own properties, separate values)
What Happens Without new
Call a constructor without new and this points to the global object (or is undefined in strict mode):
"use strict";
function Point(x, y) {
this.x = x;
this.y = y;
}
const p = Point(3, 4); // TypeError: Cannot set properties of undefined
Without strict mode, the x and y properties silently pollute the global object — a notoriously confusing bug.
Guard against misuse:
function Point(x, y) {
if (!(this instanceof Point)) {
return new Point(x, y);
}
this.x = x;
this.y = y;
}
Or just use class syntax (see trade-offs below).
Results
After reading this, you should be able to:
Predict the exact output of any
newcallExplain why methods go on
.prototyperather than inside the constructorDebug
undefinedproperty bugs caused by missingnewImplement
newmanually — useful for meta-programming and polyfills
Trade-offs
new + constructor functions vs ES6 class: Both compile to the same prototype mechanism. class syntax adds constructor enforcement (calling a class without new throws automatically), cleaner inheritance via extends, and better readability. Prefer class in new code — but understand that it's not a different system, just a cleaner syntax for the same one.
Shared prototype mutation: All instances share the same prototype object. If you add a property to Dog.prototype at runtime, every existing instance immediately sees it. Useful sometimes, surprising when it happens by accident.
Performance: Prototype-based method lookup is fast but not free. For extremely hot paths (millions of calls), inlining methods directly on instances can be faster, at the cost of memory.
new returning an explicit object: If the constructor returns a plain object, new gives you that object instead of the auto-created one. This breaks instanceof and is almost always a mistake.
function Weird() {
return { surprise: true }; // new Weird() instanceof Weird → false
}
Conclusion
The new keyword is four steps in sequence: create, link, run, return. Every behavior you observe — instanceof, inherited methods, shared state, constructor-less calls breaking — follows directly from those four steps. Internalize them and constructor functions become completely predictable.
The natural next step is ES6 class syntax, which wraps this mechanism in a safer, more expressive API without changing the underlying model.
Further Reading
MDN:
Object.create()— for the prototype-only pattern without constructors


