How the prototype chain works in JavaScript
In JavaScript an object can call a method it doesn't own. Class methods are defined once, on a shared prototype, and instances reach for them through a link. The mechanism is called the prototype chain. A note on how it works and why.
In JavaScript an object can call a method it doesn’t own. The Circle class defines an area method, but when you do new Circle(), that method doesn’t end up on the new instance - it’s defined once, on a shared Circle.prototype object. Each instance only points to that object through a link - the spec calls it [[Prototype]].
class Circle {
constructor(r) {
this.r = r;
}
area() {
return Math.PI * this.r ** 2;
}
}
const a = new Circle(1);
a.hasOwnProperty("area"); // false
a.area(); // but this worksarea doesn’t exist on a, yet a.area() returns a result. That’s because JavaScript doesn’t look up properties only on the object itself - it walks the chain of prototypes, upward, until it finds the property or
reaches the end. The mechanism is called the prototype chain and it’s the
foundation of inheritance in JS.
#Under the hood, class is an object
The key to the prototype chain is one thought: a class in JavaScript is also an object - with its methods sitting on the prototype field. When you write class Circle { area() {} }, the engine creates a Circle.prototype object and places the area method on it. Every new Circle instance gets a link to that object - its own prototype.
This model is called prototypal inheritance.
#Lookup along the chain
It’s one simple mechanism. When the engine reads a.area():
- It first looks at the object
aitself. Noareathere. - It follows the link to the prototype (
Circle.prototype). Found. Returns the result.
If the prototype doesn’t have it either, the engine goes one level up - to the prototype of the prototype - and keeps going until the end of the chain. The end is Object.prototype, whose own prototype is already null. If the property is never found along the way, you get undefined.
That’s the prototype chain: a sequence of objects the engine walks through when it looks up properties. The link itself you can read at runtime via Object.getPrototypeOf(obj) - the canonical access to [[Prototype]] (the old __proto__ alias is non-standard and discouraged by MDN).
The consequence: one method in memory serves any number of instances. Whether you have ten Circle instances or a million of them - area exists once, on a single Circle.prototype. Every instance just reaches for it through the link - it doesn’t carry the method itself.
#Object.create() - inheritance without new
The cleanest way to show this mechanism is Object.create() - an ES5 method that does exactly two things: it creates a new empty object and sets its link to whatever object you pass in.
const animal = {
describe() {
return `I'm ${this.name}.`;
},
};
const dog = Object.create(animal);
dog.name = "Rex";
dog.describe(); // "I'm Rex."There’s no class here, and no new. dog is just an object with a link to animal. When you call dog.describe(), the engine doesn’t find describe on dog itself - it follows the link to animal, finds it, calls it.
The same operation runs under the hood when you use class extends - it’s one of the things extends hides behind itself:
ChildClass.prototype = Object.create(ParentClass.prototype);That’s a single line extends adds for you. It tells the engine to link ChildClass.prototype to ParentClass.prototype. Thanks to that, the engine reaches the parent’s methods through the same mechanism - by following that very link upward.
#Closing thought
Classes in JavaScript are syntactic sugar for prototypal inheritance - not a new mechanism. That’s why class Circle extends Shape ultimately comes down to two objects (Circle.prototype and Shape.prototype) connected by a link, with the engine walking up that link when it looks for missing properties.
You meet the same mechanism with constructors - I described it step by step in the post on new, where you can see exactly what new does with the prototype of a fresh object and why ES2015 classes are the same mechanism with nicer syntax.