Prototypal vs Classical Inheritance in JavaScript

Understanding inheritance in JavaScript requires distinguishing between classical inheritance and prototypal inheritance. While classical inheritance relies on classes acting as blueprints to instantiate new objects, JavaScript natively uses prototypal inheritance, where objects inherit directly from other objects through a prototype chain. Although JavaScript introduced the class keyword in ES6, it remains a syntactic layer over its native prototypal system rather than a true implementation of classical OOP.

Fundamental Concepts

Classical Inheritance

In traditional object-oriented languages like Java, C++, or C#, inheritance is class-based: * Classes as Blueprints: A class defines the structure, properties, and methods. Instances are concrete allocations of memory created from these blueprints. * Rigid Hierarchies: Relationships are defined at compile time, leading to deep, immutable class hierarchies. * Instantiation Copying: When an instance is created, methods and attributes are typically copied or bound to the instance according to the class specification.

Prototypal Inheritance

JavaScript relies on a direct object-to-object linkage model: * Objects Inherit from Objects: There are no abstract blueprints required. Any existing object can serve as a prototype for another object. * Delegation via Prototype Chain: When a property or method is accessed on an object, JavaScript checks the object itself. If not found, it traverses up the [[Prototype]] chain until it finds the property or reaches null. * Dynamic and Mutable: Prototypes can be augmented or modified at runtime, and all descendant objects immediately reflect these changes.

Key Differences

Feature Classical Inheritance Prototypal Inheritance (JavaScript)
Inheritance Target Classes inherit from other classes Objects inherit from other objects
Instantiation Uses constructors/classes to build instances Uses object delegation (Object.create, prototype)
Structure Static and compile-time defined Dynamic and runtime modifiable
Coupling High coupling via strict hierarchies Loose coupling via composition and delegation

How Prototypal Inheritance Works in Practice

JavaScript implements prototypal inheritance through the prototype chain. When using standard object creation methods, delegation occurs automatically:

const vehicle = {
  drive() {
    return "Moving forward";
  }
};

// Inherit directly from vehicle
const car = Object.create(vehicle);
car.doors = 4;

console.log(car.doors); // 4 (own property)
console.log(car.drive()); // "Moving forward" (delegated to vehicle)

The ES6 class Syntax

The introduction of the class syntax in ES6 did not alter JavaScript’s underlying execution model.

class Animal {
  constructor(name) {
    this.name = name;
  }
  speak() {
    return `${this.name} makes a noise.`;
  }
}

Under the hood, Animal remains a function, and speak is attached to Animal.prototype. The class keyword simplifies syntax for developers familiar with classical languages, but the engine continues to execute standard prototypal delegation.