← Back to Chapters

JavaScript Inheritance

? JavaScript Inheritance

⚡ Quick Overview

Inheritance lets one object or class reuse the properties and methods of another. In JavaScript this is implemented through the prototype chain and, in modern code, the class and extends keywords. It helps you build reusable, organized object hierarchies.

? Key Concepts

  • Prototypal inheritance – objects inherit directly from other objects via the prototype chain.
  • Constructor functions – pre-ES6 way to define “types” and share methods via Function.prototype.
  • Class-based syntax – ES6 class, extends, and instance methods provide a cleaner syntax over prototypes.
  • super keyword – used inside subclasses to call parent constructors and methods.
  • Reusability – define common behavior once in a base type and reuse it across children.

? Syntax & Theory

Every JavaScript object has an internal link to another object called its prototype. When you access a property or method, JavaScript first looks on the object itself, then walks up the prototype chain until it finds it.

ES6 class syntax is syntactic sugar over this prototypal model. A class body defines a constructor and methods that are actually stored on ClassName.prototype. The extends keyword links the prototype of the child class to the prototype of the parent class.

The super keyword lets child classes delegate work back to the parent class, such as initializing shared fields or reusing parent logic from overridden methods.

? Code Examples

?️ Prototypal Inheritance with Object.create()

Here, child inherits directly from parent using the prototype chain.

? View Code Example
const parent = {
  greet() {
    console.log("Hello from parent");
  }
};

const child = Object.create(parent);
child.greet(); // Hello from parent

? Constructor Function Inheritance (Pre-ES6)

Before classes, constructor functions plus prototypes were the main pattern for inheritance.

? View Code Example
function Animal(name) {
  this.name = name;
}

Animal.prototype.speak = function() {
  console.log(this.name + " makes a sound.");
};

function Dog(name) {
  // Call parent constructor with current context
  Animal.call(this, name);
}

// Inherit from Animal
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.speak = function() {
  console.log(this.name + " barks.");
};

const d = new Dog("Rex");
d.speak(); // Rex barks.

? Class-Based Inheritance (ES6+)

ES6 classes give a more familiar OOP syntax, but they still use prototypes under the hood.

? View Code Example
class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(this.name + " makes a sound.");
  }
}

class Dog extends Animal {
  speak() {
    console.log(this.name + " barks.");
  }
}

const dog = new Dog("Buddy");
dog.speak(); // Buddy barks.

? Using super in a Subclass

super lets you call parent constructors and methods from within a child class.

? View Code Example
class Vehicle {
  constructor(type) {
    this.type = type;
  }

  move() {
    console.log(this.type + " is moving");
  }
}

class Car extends Vehicle {
  constructor(type, brand) {
    super(type); // Call parent constructor
    this.brand = brand;
  }

  move() {
    super.move(); // Call parent method
    console.log(this.brand + " car drives smoothly");
  }
}

const car = new Car("Vehicle", "Toyota");
car.move();
// Vehicle is moving
// Toyota car drives smoothly

? Live Output & Explanation

? What the Examples Do

  • Prototypal example: child.greet() is not defined on child itself, so JavaScript looks up the prototype chain and finds greet on parent, logging "Hello from parent".
  • Constructor function example: Dog calls Animal.call(this, name) to copy base properties, then inherits methods from Animal.prototype. Overriding speak in Dog.prototype changes the behavior to log "Rex barks.".
  • Class example: Dog extends Animal sets up the prototype chain automatically. Calling dog.speak() uses the overridden method in the child class, logging "Buddy barks.".
  • super example: Car.move() first calls the parent Vehicle.move() (logs "Vehicle is moving"), then adds its own log "Toyota car drives smoothly".

? Use Cases & When to Use Inheritance

  • When multiple types share common properties or behavior (e.g., Animal → Dog, Cat).
  • When you want a clear “is-a” relationship between entities (e.g., Car is a Vehicle).
  • When extending built-in types, such as class MyArray extends Array to add custom helpers.
  • When you need to reuse and specialize logic rather than duplicate it in multiple places.

?️ Interactive Example

Click the button to create a Dog that inherits from Animal and see the overridden speak() method in action.

? Demo Output

Click the button above to run the demo.

? Tips & Best Practices

  • Prefer class, extends, and super for clean, modern inheritance syntax.
  • Use prototypes (or class methods) for sharing behavior so methods are not recreated per instance.
  • Avoid very deep inheritance hierarchies; they are hard to understand and maintain. Consider composition when things get complex.
  • When manually using Object.create, remember to fix the constructor property if you rely on it.
  • When overriding methods in subclasses, call super.methodName() if you still need the parent behavior.

? Try It Yourself

  • Create a base class Shape with a getArea() method, then extend it with Circle and Rectangle, each implementing their own area calculation.
  • Write a constructor function Employee with shared methods on Employee.prototype, then create Manager that inherits from it using Object.create.
  • Experiment with super inside a subclass method to call the parent’s implementation and then add extra behavior.
  • Extend a built-in type (e.g., Array) with a custom class that adds a helpful utility method.