# What Is the Prototype Chain in JavaScript and How Does Inheritance Work?

> Understand the JavaScript prototype chain and how inheritance works. Learn about the delegation mechanism and property lookups the engine uses to resolve values.

- Repository: [Leonardo Maldonado/33-js-concepts](https://github.com/leonardomso/33-js-concepts)
- Tags: deep-dive
- Published: 2026-03-03

---

**The prototype chain in JavaScript is a delegation mechanism where every object has an internal `[[Prototype]]` reference (accessible via `Object.getPrototypeOf`) that links to another object, forming a chain that the engine traverses upward to resolve property lookups until reaching `Object.prototype` whose prototype is `null`.**

The `leonardomso/33-js-concepts` repository demonstrates these mechanics through comprehensive test suites. Understanding the prototype chain in JavaScript is fundamental to working with object-oriented patterns, as it governs how properties are inherited, how `instanceof` operates, and how memory is shared across instances.

## Understanding the Core Prototype Chain Mechanism

Every JavaScript object contains an internal slot called `[[Prototype]]`, exposed in code through `Object.getPrototypeOf(obj)` or the legacy `__proto__` accessor. When you access a property or method, the engine first checks the object itself; if the property is missing, it follows the `[[Prototype]]` link to the next object and repeats the search. This continues recursively until the property is found or the chain terminates at `Object.prototype` (whose own prototype is `null`).

This linked structure is the **prototype chain**. It enables **delegation-based inheritance** rather than class-based copying, meaning objects inherit references to shared methods rather than holding separate copies.

## Class-Based Inheritance and the Double Link

When you declare `class Child extends Parent`, JavaScript sets up two distinct prototype relationships:

1. **Instance delegation**: `new Child()` produces an object whose `[[Prototype]]` points to `Child.prototype`
2. **Prototype delegation**: `Child.prototype`'s `[[Prototype]]` points to `Parent.prototype`

As verified in [`tests/object-oriented/inheritance-polymorphism/inheritance-polymorphism.test.js`](https://github.com/leonardomso/33-js-concepts/blob/main/tests/object-oriented/inheritance-polymorphism/inheritance-polymorphism.test.js) (lines 3-10), this creates a three-tier chain for any `Warrior` instance extending `Character`:

```javascript
Object.getPrototypeOf(warrior) === Warrior.prototype
Object.getPrototypeOf(Warrior.prototype) === Character.prototype
Object.getPrototypeOf(Character.prototype) === Object.prototype

```

### Method Resolution Order

Property lookup follows this chain sequentially. The test suite (lines 15-21) confirms that `Warrior.prototype.hasOwnProperty('attack')` returns `true` because `attack` is defined directly on the child prototype, while `Character.prototype.hasOwnProperty('introduce')` returns `true` for inherited methods. If `warrior.introduce()` is called, the engine finds it on `Character.prototype` after failing to locate it on `Warrior.prototype` or the `warrior` instance itself.

## The `super` Keyword and Static Inheritance

The `super` keyword provides explicit access to the parent class within subclass definitions. Inside constructors, `super(args)` invokes the parent constructor. Inside methods, `super.method()` climbs the prototype chain to execute the parent implementation.

The repository's `ExtendedWarrior` example (lines 50-60) demonstrates this pattern:

```javascript
class ExtendedWarrior extends Character {
  attack() {
    const base = super.attack()  // Calls Character.prototype.attack
    return `${base} With an Axe!`
  }
}

```

### Static Member Inheritance

Static methods and properties live on the constructor functions themselves, not on `.prototype`. When `class Warrior extends Character` is declared, JavaScript sets `Object.getPrototypeOf(Warrior) === Character`, enabling static inheritance. The test suite (lines 31-35) validates that `Warrior.createRandom()` works because it inherits the static `createRandom` method from `Character` via the constructor's prototype chain.

## Checking Prototype Relationships with `instanceof`

The `instanceof` operator checks the entire prototype chain, not just the immediate constructor. As shown in the test file (lines 68-74), a `Warrior` instance returns `true` for `warrior instanceof Warrior`, `warrior instanceof Character`, and `warrior instanceof Object` because all three prototypes exist in the object's chain:

```javascript
console.log(warrior instanceof Warrior)   // true
console.log(warrior instanceof Character) // true
console.log(warrior instanceof Object)    // true

```

This works by verifying whether `Warrior.prototype` (or any object in its prototype chain) exists in `warrior`'s `[[Prototype]]` chain.

## Practical Implementation Example

The following runnable code extracted from the repository demonstrates the complete inheritance pattern, including constructor delegation, method overriding, and polymorphic behavior:

```javascript
// Base class definition
class Character {
  constructor(name, health = 100) {
    this.name = name
    this.health = health
  }
  
  introduce() {
    return `I am ${this.name} with ${this.health} HP`
  }
  
  attack() {
    return `${this.name} attacks!`
  }
  
  static createRandom() {
    const names = ['Hero', 'Villain', 'Sidekick']
    return new this(names[Math.floor(Math.random() * names.length)])
  }
}

// Subclass declaration
class Warrior extends Character {
  constructor(name) {
    super(name, 150)  // Delegates to Character constructor
    this.rage = 0
  }
  
  attack() {
    // Overrides parent method
    return `${this.name} swings a mighty sword!`
  }
  
  battleCry() {
    this.rage += 10
    return `${this.name} roars! Rage: ${this.rage}`
  }
}

// Prototype chain verification
const conan = new Warrior('Conan')
console.log(Object.getPrototypeOf(conan) === Warrior.prototype)           // true
console.log(Object.getPrototypeOf(Warrior.prototype) === Character.prototype) // true

// Property lookup demonstration
console.log(conan.attack())      // Uses Warrior.prototype.attack
console.log(conan.introduce())   // Uses Character.prototype.introduce (inherited)

// Static inheritance
const randomWarrior = Warrior.createRandom()
console.log(randomWarrior instanceof Warrior)   // true
console.log(randomWarrior instanceof Character) // true

// Polymorphic function operating on shared interface
function executeBattle(chars) {
  return chars.map(c => c.attack())
}
console.log(executeBattle([new Warrior('A'), new Character('B')]))
// Output: ["A swings a mighty sword!", "B attacks!"]

```

## Summary

- Every JavaScript object links to another object via its internal `[[Prototype]]` slot, forming the prototype chain.
- Class inheritance using `extends` creates two links: one from instance to child prototype, and one from child prototype to parent prototype.
- The `super` keyword explicitly accesses parent class constructors and methods, while static members inherit through the constructor's own prototype chain.
- `instanceof` traverses the entire prototype chain to determine membership, returning `true` for any ancestor constructor in the hierarchy.
- This delegation model enables memory-efficient method sharing and polymorphic design patterns, as demonstrated in [`tests/object-oriented/inheritance-polymorphism/inheritance-polymorphism.test.js`](https://github.com/leonardomso/33-js-concepts/blob/main/tests/object-oriented/inheritance-polymorphism/inheritance-polymorphism.test.js).

## Frequently Asked Questions

### What is the difference between `__proto__` and `prototype`?

`__proto__` is an accessor property that exposes the internal `[[Prototype]]` of an instance object, while `prototype` is a regular property on constructor functions that becomes the `[[Prototype]]` of instances created with that constructor. In modern JavaScript, use `Object.getPrototypeOf()` and `Object.setPrototypeOf()` instead of `__proto__` for better performance and standards compliance.

### How does the `instanceof` operator actually work?

`instanceof` checks if the constructor's `prototype` property exists anywhere in the object's prototype chain. Specifically, `obj instanceof Constructor` evaluates whether `Constructor.prototype` is reachable from `obj` following the `[[Prototype]]` links upward until `null`. This is why a `Warrior` instance returns `true` for both `Warrior` and `Character` checks.

### Can you modify an object's prototype after it is created?

Yes, though it is generally discouraged for performance reasons. You can change an object's prototype using `Object.setPrototypeOf(obj, newProto)` or by setting `__proto__`, but doing so invalidates engine optimizations. It is better to set the prototype at creation time using `Object.create(newProto)` or class `extends` declarations.

### Why does static inheritance work differently than instance inheritance?

Static members belong to the constructor function itself, not the prototype object. When `class B extends A` is parsed, JavaScript internally performs `Object.setPrototypeOf(B, A)`, linking the constructor functions in a chain parallel to the instance prototype chain. This allows `B` to access static methods defined on `A`, such as `B.createRandom()` delegating to `A.createRandom()`.