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.

JavaScriptintermediate
14 min read

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":

javascriptjavascript
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 chain

The chain lookup: look on dog → not found → look on animal → found → call it with this = dog.

Property Lookup Algorithm

When accessing obj.property, JavaScript:

  1. Checks if obj has own property property
  2. If not, moves to obj[[Prototype]] (the prototype object)
  3. Checks if the prototype has own property property
  4. If not, moves to the prototype's [[Prototype]]
  5. Continues until the property is found or reaches null
javascriptjavascript
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:

javascriptjavascript
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 directly

The Prototype Chain of a Plain Object

Plain object literals have Object.prototype as their prototype:

javascriptjavascript
const obj = { name: "test" };
 
// Prototype chain: obj → Object.prototype → null
console.log(Object.getPrototypeOf(obj) === Object.prototype); // true
console.log(Object.getPrototypeOf(Object.prototype));         // null

That 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:

javascriptjavascript
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.prototype

The chain: dogAnimal.prototypeObject.prototypenull

Object.create for Prototypal Inheritance

Object.create(proto) creates a new object with proto as its [[Prototype]], without involving constructor functions:

javascriptjavascript
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); // true

class Syntax and the Prototype Chain

ES6 class is syntax sugar over prototype-based inheritance. Under the hood, it creates the same chain:

javascriptjavascript
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.prototype

Shadowing: 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:

javascriptjavascript
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/OperatorWhat It Does
obj.hasOwnProperty(key)True only if key is directly on obj, not prototype
key in objTrue 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 ConstructorTrue if Constructor.prototype is in obj's chain
javascriptjavascript
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));     // true

Null Prototype Objects

Objects with no prototype skip Object.prototype methods entirely:

javascriptjavascript
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.constructor

Prototype 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:

javascriptjavascript
// 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 — OK
Rune AI

Rune AI

Key Insights

  • Property lookup climbs the chain: Accessing obj.prop first 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
RunePowered by Rune AI

Frequently Asked Questions

Is JavaScript prototype chain the same as classical inheritance?

No. Classical inheritance (Java, C++) copies the class blueprint into each instance. JavaScript uses delegation — the instance does not have a copy of prototype methods; it delegates method calls up the chain to the prototype at runtime. This means changing a prototype method affects all instances inheriting from it, even those already created.

Can I create an infinite prototype chain?

No. `Object.setPrototypeOf` checks for cycles and throws a `TypeError` if you try to create a circular prototype chain.

What is the difference between __proto__ and prototype?

`__proto__` (or accessed via `Object.getPrototypeOf`) is the actual `[[Prototype]]` link on an instance — it points to the prototype object the instance inherits from. `.prototype` is a property on constructor functions — it is the object that will become the `[[Prototype]]` of instances created with `new`. See [__proto__ vs prototype](/tutorials/programming-languages/javascript/js-proto-vs-prototype-what-is-the-difference) for a complete comparison.

Do class methods live on prototype or on the instance?

Class methods (defined without `=` in the class body) live on the class's `prototype`. Only properties assigned in the constructor (`this.x = value`) live on the instance. This is why all instances share the same method function objects — they reference the same function via the prototype chain.

Is for...in affected by the prototype chain?

Yes. `for...in` iterates over all enumerable properties in the entire prototype chain, not just own properties. Use `for...of` with `Object.keys()` or `Object.entries()` to iterate only own properties, or guard with `hasOwnProperty` inside `for...in`.

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.