The JavaScript Prototype Chain: Complete Guide
Master the JavaScript prototype chain from the ground up. Learn how property lookup works, how objects link via [[Prototype]], inheritance patterns using Object.create and classes, and how the chain affects performance.
JavaScript's inheritance model is fundamentally different from classical OOP languages. Instead of classes copying their blueprint into instances, JavaScript objects have a live linkage to other objects — a prototype chain. When you access a property on an object and it is not found, JavaScript automatically walks up this chain looking for it. Understanding this mechanism is essential for understanding how methods, inheritance, and class syntax actually work under the hood.
Every Object Has a Prototype
Every JavaScript object has an internal slot called [[Prototype]] that points to another object (or null). This link is the "prototype chain":
const animal = {
breathe() {
return `${this.name} breathes`;
},
};
const dog = {
name: "Rex",
};
// Set dog's prototype to animal
Object.setPrototypeOf(dog, animal);
// dog doesn't have breathe() directly, but the chain finds it
console.log(dog.breathe()); // "Rex breathes" — found via prototype chainThe chain lookup: look on dog → not found → look on animal → found → call it with this = dog.
Property Lookup Algorithm
When accessing obj.property, JavaScript:
- Checks if
objhas own propertyproperty - If not, moves to
obj[[Prototype]](the prototype object) - Checks if the prototype has own property
property - If not, moves to the prototype's
[[Prototype]] - Continues until the property is found or reaches
null
const a = { x: 1 };
const b = Object.create(a); // b's [[Prototype]] = a
const c = Object.create(b); // c's [[Prototype]] = b
b.y = 2;
console.log(c.x); // 1 — found at a (2 hops up)
console.log(c.y); // 2 — found at b (1 hop up)
console.log(c.z); // undefined — not found anywhere (chain ends at null)You can inspect own properties vs inherited ones:
console.log(c.hasOwnProperty("y")); // false — y is on b, not c
console.log(c.hasOwnProperty("z")); // false — nowhere
console.log(b.hasOwnProperty("y")); // true — y is on b directlyThe Prototype Chain of a Plain Object
Plain object literals have Object.prototype as their prototype:
const obj = { name: "test" };
// Prototype chain: obj → Object.prototype → null
console.log(Object.getPrototypeOf(obj) === Object.prototype); // true
console.log(Object.getPrototypeOf(Object.prototype)); // nullThat is why all objects have methods like .toString(), .hasOwnProperty(), .valueOf() — they are inherited from Object.prototype.
Functions and the .prototype Property
Functions have a special prototype property (not to be confused with [[Prototype]]). When a function is used as a constructor with new, the created object's [[Prototype]] is set to the function's .prototype:
function Animal(name) {
this.name = name;
}
Animal.prototype.breathe = function () {
return `${this.name} breathes`;
};
const dog = new Animal("Rex");
// dog's [[Prototype]] === Animal.prototype
console.log(Object.getPrototypeOf(dog) === Animal.prototype); // true
console.log(dog.breathe()); // "Rex breathes" — from Animal.prototypeThe chain: dog → Animal.prototype → Object.prototype → null
Object.create for Prototypal Inheritance
Object.create(proto) creates a new object with proto as its [[Prototype]], without involving constructor functions:
const vehicleProto = {
describe() {
return `${this.type} with ${this.wheels} wheels`;
},
start() {
return `${this.type} starting...`;
},
};
// Create a car that inherits from vehicleProto
const car = Object.create(vehicleProto);
car.type = "car";
car.wheels = 4;
console.log(car.describe()); // "car with 4 wheels" — from vehicleProto
console.log(car.start()); // "car starting..." — from vehicleProto
// Create a motorcycle that also inherits
const moto = Object.create(vehicleProto);
moto.type = "motorcycle";
moto.wheels = 2;
// Verify the chain
console.log(Object.getPrototypeOf(car) === vehicleProto); // true
console.log(Object.getPrototypeOf(moto) === vehicleProto); // trueclass Syntax and the Prototype Chain
ES6 class is syntax sugar over prototype-based inheritance. Under the hood, it creates the same chain:
class Animal {
constructor(name) {
this.name = name;
}
breathe() {
return `${this.name} breathes`;
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
bark() {
return `${this.name} barks`;
}
}
const rex = new Dog("Rex", "Labrador");
// Chain: rex → Dog.prototype → Animal.prototype → Object.prototype → null
console.log(Object.getPrototypeOf(rex) === Dog.prototype); // true
console.log(Object.getPrototypeOf(Dog.prototype) === Animal.prototype); // true
console.log(Object.getPrototypeOf(Animal.prototype) === Object.prototype); // true
console.log(rex.breathe()); // "Rex breathes" — found at Animal.prototype
console.log(rex.bark()); // "Rex barks" — found at Dog.prototype
console.log(rex.toString()); // "[object Object]" — found at Object.prototypeShadowing: Own Properties Override Prototype Properties
When an object has an own property with the same name as one in the prototype chain, the own property shadows the prototype's:
const base = { greet() { return "Hello from base"; } };
const derived = Object.create(base);
console.log(derived.greet()); // "Hello from base" — from prototype
// Shadow the method on the instance
derived.greet = function () { return "Hello from derived"; };
console.log(derived.greet()); // "Hello from derived" — own property wins
// Base is unchanged
const other = Object.create(base);
console.log(other.greet()); // "Hello from base"Checking the Chain
| Method/Operator | What It Does |
|---|---|
obj.hasOwnProperty(key) | True only if key is directly on obj, not prototype |
key in obj | True if key exists anywhere in the chain |
Object.getPrototypeOf(obj) | Returns the prototype object (or null) |
proto.isPrototypeOf(obj) | True if proto is anywhere in obj's chain |
obj instanceof Constructor | True if Constructor.prototype is in obj's chain |
const a = { x: 1 };
const b = Object.create(a);
b.y = 2;
console.log("x" in b); // true — found in chain
console.log("y" in b); // true — own property
console.log(b.hasOwnProperty("x")); // false — x is on 'a'
console.log(b.hasOwnProperty("y")); // true
console.log(a.isPrototypeOf(b)); // trueNull Prototype Objects
Objects with no prototype skip Object.prototype methods entirely:
const plain = Object.create(null);
plain.key = "value";
console.log(plain.toString); // undefined — no Object.prototype
console.log(plain.hasOwnProperty); // undefined
// Useful for safe dictionaries (no inherited key collisions):
const safeMap = Object.create(null);
safeMap["constructor"] = "safe"; // No conflict with Object.constructorPrototype Mutation Performance Warning
Modifying an object's [[Prototype]] after creation (via Object.setPrototypeOf) de-optimizes the object in V8 and other engines. The engine must exit fast property access paths:
// SLOW: avoid in hot paths
Object.setPrototypeOf(existingObject, newProto); // De-optimizes the object
// FAST: set prototype at creation time
const obj = Object.create(proto); // Prototype set at creation — OK
class Derived extends Base {} // class syntax — OKRune AI
Key Insights
- Property lookup climbs the chain: Accessing
obj.propfirst checks the object, then its prototype, then its prototype's prototype, all the way to null - Object.prototype is at the top: Virtually all objects ultimately inherit from Object.prototype, which is why methods like toString and hasOwnProperty are universally available
- class syntax is prototype sugar: Every class creates a prototype chain structurally identical to what manual prototype assignment produces — the syntax is different but the runtime model is identical
- Own properties shadow prototype properties: Setting a property directly on an instance creates an own property that takes precedence over any identically-named property anywhere in the prototype chain
- for...in walks the whole chain: Always use hasOwnProperty or Object.keys/entries when you only want the object's own enumerable properties
Frequently Asked Questions
Is JavaScript prototype chain the same as classical inheritance?
Can I create an infinite prototype chain?
What is the difference between __proto__ and prototype?
Do class methods live on prototype or on the instance?
Is for...in affected by the prototype chain?
Conclusion
The prototype chain is JavaScript's inheritance mechanism: objects link to other objects via [[Prototype]], and property lookups walk this chain until the property is found or null is reached. class syntax creates the same chains in a more readable way. Understanding the chain explains how instanceof works, why hasOwnProperty differs from in, and how method sharing between instances works. The natural next step is understanding __proto__ vs prototype to distinguish these two closely-related but different concepts.
More in this topic
OffscreenCanvas API in JS for UI Performance
Master the OffscreenCanvas API to offload rendering from the main thread. Covers worker-based 2D and WebGL rendering, animation loops inside workers, bitmap transfer, double buffering, chart rendering pipelines, image processing, and performance measurement strategies.
Advanced Web Workers for High Performance JS
Master Web Workers for truly parallel JavaScript execution. Covers dedicated and shared workers, structured cloning, transferable objects, SharedArrayBuffer with Atomics, worker pools, task scheduling, Comlink RPC patterns, module workers, and performance profiling strategies.
JavaScript Macros and Abstract Code Generation
Master JavaScript code generation techniques for compile-time and runtime metaprogramming. Covers AST manipulation, Babel plugin authorship, tagged template literals as macros, code generation pipelines, source-to-source transformation, compile-time evaluation, and safe eval alternatives.