Home ยป JavaScript Inheritance Tutorial with Examples

JavaScript Inheritance Tutorial with Examples

Inheritance is a core concept in object-oriented programming (OOP) that allows one class to inherit the properties and methods of another class.

In JavaScript, inheritance enables code reuse and provides a way to create hierarchical class structures where subclasses (derived classes) inherit and possibly override behavior from their parent (base) classes.

In this tutorial, you'll learn:

What is inheritance in JavaScript?
Prototypal inheritance in JavaScript
Inheritance using ES6 classes
Overriding methods in subclasses
Calling parent class methods using super
Practical examples of inheritance in JavaScript

1. What is Inheritance in JavaScript?

Inheritance is a mechanism where one object can acquire the properties and methods of another object. In JavaScript, this is commonly done via prototypes or ES6 classes.

he primary purpose of inheritance is to promote code reuse and create a hierarchy where child objects or classes can inherit behavior from parent objects or classes.

JavaScript supports two types of inheritance:

Prototypal inheritance (using prototype chains).
Class-based inheritance (introduced in ES6 with class and extends keywords).

2. Prototypal Inheritance in JavaScript

JavaScript is a prototype-based language, which means that objects can inherit directly from other objects. Each object has an internal property called [[Prototype]] (commonly accessed using __proto__ or Object.getPrototypeOf()), which points to its prototype object.

Example 1: Basic Prototypal Inheritance

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

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

function Dog(name) {
    Animal.call(this, name);  // Call the Animal constructor
}

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

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

const dog = new Dog("Rex");
dog.speak();  // Output: Rex barks.

In this example:

We define a constructor function Animal with a method speak in its prototype.
The Dog constructor inherits from Animal using Object.create(Animal.prototype), which sets up the inheritance chain.
We override the speak method in the Dog prototype to provide a more specific behavior for Dog.

3. Inheritance Using ES6 Classes

With ES6 (ECMAScript 2015), JavaScript introduced class syntax, which provides a more structured way to define and manage inheritance using extends and super.

Example 2: Inheritance Using ES6 Classes

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

    speak() {
        console.log(`${this.name} makes a sound.`);
    }
}

class Dog extends Animal {
    constructor(name) {
        super(name);  // Call the parent class constructor
    }

    speak() {
        console.log(`${this.name} barks.`);
    }
}

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

In this example:

The Animal class defines a base class with a constructor and a speak method.
The Dog class extends Animal using the extends keyword, inheriting all properties and methods from Animal.
The super(name) call inside the Dog constructor calls the parent class's constructor to initialize name.
The speak method is overridden in the Dog class.

4. Overriding Methods in Subclasses

In JavaScript, a subclass can override methods inherited from a parent class to provide more specific functionality. The subclass can provide its own implementation while still retaining access to the parent's methods.

Example 3: Overriding Methods in Subclasses

class Vehicle {
    constructor(brand) {
        this.brand = brand;
    }

    move() {
        console.log(`${this.brand} vehicle is moving.`);
    }
}

class Car extends Vehicle {
    move() {
        console.log(`${this.brand} car is driving.`);
    }
}

const myCar = new Car("Toyota");
myCar.move();  // Output: Toyota car is driving.

In this example:

The Vehicle class defines a move method.
The Car class inherits from Vehicle and overrides the move method with its own behavior.
When move() is called on the myCar object, it uses the overridden method from the Car class.

5. Calling Parent Class Methods Using super

The super keyword is used to access the parent class's methods or constructor from the subclass. This is useful when you want to extend or enhance the functionality of a parent method rather than completely overriding it.

Example 4: Using super to Call Parent Methods

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

    speak() {
        console.log(`${this.name} makes a sound.`);
    }
}

class Dog extends Animal {
    constructor(name, breed) {
        super(name);  // Call the parent class constructor
        this.breed = breed;
    }

    speak() {
        super.speak();  // Call the parent class's speak method
        console.log(`${this.name} is a ${this.breed} and barks.`);
    }
}

const dog = new Dog("Max", "Golden Retriever");
dog.speak();
// Output: Max makes a sound.
//         Max is a Golden Retriever and barks.

In this example:

The super.speak() call in the Dog class allows the speak method from the Animal class to be executed first, followed by the Dog class's specific behavior.

6. Practical Examples of Inheritance in JavaScript

Example 5: Inheritance in a Shape System

Consider a system where different shapes (e.g., circle, rectangle) share some common properties but have different implementations for calculating area.

class Shape {
    constructor(color) {
        this.color = color;
    }

    getArea() {
        return 0;  // Default implementation (abstract)
    }

    describe() {
        console.log(`This is a ${this.color} shape.`);
    }
}

class Circle extends Shape {
    constructor(radius, color) {
        super(color);
        this.radius = radius;
    }

    getArea() {
        return Math.PI * this.radius ** 2;
    }
}

class Rectangle extends Shape {
    constructor(width, height, color) {
        super(color);
        this.width = width;
        this.height = height;
    }

    getArea() {
        return this.width * this.height;
    }
}

const circle = new Circle(5, "red");
console.log(`Circle area: ${circle.getArea()}`);  // Output: Circle area: 78.53981633974483
circle.describe();  // Output: This is a red shape.

const rectangle = new Rectangle(4, 6, "blue");
console.log(`Rectangle area: ${rectangle.getArea()}`);  // Output: Rectangle area: 24
rectangle.describe();  // Output: This is a blue shape.

In this example:

Shape is a base class that provides a default getArea method and a common describe method for all shapes.
Circle and Rectangle inherit from Shape and override the getArea method to provide their specific calculations.
The describe method is inherited and used as-is from the Shape class.

Example 6: Inheritance in a User Management System

In a user management system, you may have different types of users (e.g., Admin, Guest) with different permissions, but they share common properties.

class User {    constructor(name, email) {
        this.name = name;
        this.email = email;
    }

    getInfo() {
        return `${this.name} (${this.email})`;
    }
}

class Admin extends User {
    constructor(name, email, role) {
        super(name, email);
        this.role = role;
    }

    getInfo() {
        return `${super.getInfo()} - Role: ${this.role}`;
    }
}

class Guest extends User {
    constructor(name, email) {
        super(name, email);
    }

    getInfo() {
        return `${super.getInfo()} - Guest User`;
    }
}

const admin = new Admin("Alice", "alice@example.com", "Administrator");
console.log(admin.getInfo());  // Output: Alice (alice@example.com) - Role: Administrator

const guest = new Guest("Bob", "bob@example.com");
console.log(guest.getInfo());  // Output: Bob (bob@example.com) - Guest User

In this example:

User is the base class that holds common user information.
Admin and Guest are specialized user types that inherit from User but override the getInfo method to provide additional information.

Conclusion

Inheritance is a powerful feature in JavaScript that allows classes to reuse code and share behavior. Here's a summary of what you've learned:

Prototypal inheritance: The core of inheritance in JavaScript, where objects inherit properties and methods from other objects.
ES6 class-based inheritance: Provides a more familiar syntax for creating classes and handling inheritance using extends and super.
Overriding methods: Subclasses can override parent class methods to provide their own specific behavior.
Calling parent methods: Using super allows a subclass to call methods from its parent class.

By mastering inheritance, you can write more modular, reusable, and maintainable code in JavaScript!

You may also like