Understanding Prototypal Inheritance in JavaScript

Published on: by Dr. Talib

Unlike classical inheritance in languages like Java or C++, JavaScript uses a model called **prototypal inheritance**. In this model, objects inherit directly from other objects. Every JavaScript object has a private property which holds a link to another object called its **prototype**. That prototype object has a prototype of its own, and so on, until an object is reached with `null` as its prototype. This linked sequence is known as the **prototype chain**.


The Prototype Chain in Action

The Concept: When you try to access a property on an object, the JavaScript engine first checks if the property exists directly on that object. If not, it looks at the object's prototype. If it's not there, it looks at the prototype's prototype, and so on, all the way up the chain until it finds the property or reaches the end of the chain (`null`).

This is why you can call methods like .toString() on any object you create. The method doesn't exist on your object itself; it exists on Object.prototype, which is at the top of the chain for most objects.

A Simple Example:

const animal = {
  eats: true,
  walk() {
    console.log("Animal is walking.");
  }
};

// Create a new object 'dog' that inherits from 'animal'.
const dog = Object.create(animal);
dog.barks = true;

// Accessing properties:
console.log(dog.barks); // true (This is a direct property of dog)
console.log(dog.eats);  // true (This is inherited from the 'animal' prototype)
dog.walk();             // "Animal is walking." (This method is also inherited)

Try it Yourself: Paste this code into the HTML Viewer's console. The `dog` object can access properties from the `animal` object because we explicitly set `animal` as its prototype using Object.create().

Constructor Functions and the `prototype` Property

Before ES6 classes, the standard way to create objects with a shared prototype was through constructor functions.

The Key: Every function in JavaScript has a special property called prototype. This is **not** the object's actual prototype link (which is internal), but rather a blueprint. When you create a new object using the `new` keyword with a function, the new object's internal prototype is set to the constructor function's `prototype` object.

Example with a Constructor Function:

function Cat(name) {
  this.name = name;
}

// Add a method to the Cat's prototype.
// ALL instances of Cat will now inherit this method.
Cat.prototype.meow = function() {
  console.log(`${this.name} says meow!`);
};

const fluffy = new Cat('Fluffy');
const mittens = new Cat('Mittens');

fluffy.meow();  // "Fluffy says meow!"
mittens.meow(); // "Mittens says meow!"

// The .meow method is not on the fluffy object itself,
// it's on the prototype it inherited from Cat.
console.log(fluffy.hasOwnProperty('meow')); // false

This is highly efficient. Instead of creating a separate `meow` function for every single cat instance, we define it once on the prototype, and all instances share the reference to it.

ES6 Classes: Syntactic Sugar on Top

Modern JavaScript introduced the class keyword. It's crucial to understand that this **does not** introduce classical inheritance to JavaScript. ES6 classes are primarily "syntactic sugar" over the existing prototypal inheritance model. It provides a cleaner, more familiar syntax for doing the exact same thing as constructor functions.

The Same Example Using ES6 Classes:

class Cat {
  constructor(name) {
    this.name = name;
  }

  // This method is automatically placed on Cat.prototype
  meow() {
    console.log(`${this.name} says meow!`);
  }
}

const fluffy = new Cat('Fluffy');
fluffy.meow(); // "Fluffy says meow!"

This code achieves the same result as the constructor function example but is much easier to read and write for developers coming from other languages.


Why It Matters

Understanding that JavaScript's inheritance is based on a live chain of objects, not a static copy from a class blueprint, is fundamental to mastering the language. It explains how the language works at a deep level and clarifies the behavior of objects, `this` binding, and modern class syntax. It's the mechanism that makes JavaScript a flexible, dynamic, and powerful language.