JavaScript Class Inheritance

Learn how classes can inherit from other classes

🧬 What is Class Inheritance?

Inheritance allows a class to inherit properties and methods from another class. The child class gets all features of the parent class and can add its own unique features.


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

// Child class inherits from Animal
class Dog extends Animal {
    speak() {
        return `${this.name} barks`;
    }
}

const myDog = new Dog("Buddy");
console.log(myDog.speak());
                                    

Output:

Buddy barks

Key Inheritance Concepts

👨‍👦

extends

Keyword to create inheritance

class Child extends Parent {
    // Child class code
}
📞

super()

Calls parent class constructor

constructor(name) {
    super(name);
}
🔄

Override

Replace parent class methods

speak() {
    return "New behavior";
}

Extend

Add new methods to child class

newMethod() {
    return "Child only method";
}

🔹 Basic Inheritance Example

Let's see how a child class inherits from a parent class:

// Parent class (Base class)
class Vehicle {
    constructor(brand, year) {
        this.brand = brand;
        this.year = year;
    }
    
    start() {
        return `${this.brand} vehicle is starting...`;
    }
    
    getInfo() {
        return `${this.year} ${this.brand}`;
    }
}

// Child class (Derived class)
class Car extends Vehicle {
    constructor(brand, year, doors) {
        super(brand, year);  // Call parent constructor
        this.doors = doors;  // Add new property
    }
    
    // Override parent method
    start() {
        return `${this.brand} car engine is starting...`;
    }
    
    // Add new method
    honk() {
        return `${this.brand} car goes beep beep!`;
    }
}

// Using inheritance
const myCar = new Car("Toyota", 2023, 4);
console.log(myCar.getInfo());  // Inherited from Vehicle
console.log(myCar.start());    // Overridden method
console.log(myCar.honk());     // New method

Output:

2023 Toyota
Toyota car engine is starting...
Toyota car goes beep beep!

🔹 Using super() Keyword

The super() keyword is used to call parent class methods:

class Employee {
    constructor(name, salary) {
        this.name = name;
        this.salary = salary;
    }
    
    work() {
        return `${this.name} is working`;
    }
    
    getDetails() {
        return `Employee: ${this.name}, Salary: $${this.salary}`;
    }
}

class Manager extends Employee {
    constructor(name, salary, department) {
        super(name, salary);  // Call parent constructor
        this.department = department;
    }
    
    // Override with super call
    work() {
        const parentWork = super.work();  // Call parent method
        return `${parentWork} and managing ${this.department}`;
    }
    
    // Override with additional info
    getDetails() {
        const parentDetails = super.getDetails();  // Call parent method
        return `${parentDetails}, Department: ${this.department}`;
    }
    
    // New method
    holdMeeting() {
        return `${this.name} is holding a ${this.department} meeting`;
    }
}

const manager = new Manager("Alice", 75000, "IT");
console.log(manager.work());
console.log(manager.getDetails());
console.log(manager.holdMeeting());

Output:

Alice is working and managing IT
Employee: Alice, Salary: $75000, Department: IT
Alice is holding a IT meeting

🔹 Multi-Level Inheritance

Classes can inherit from other classes that also inherit:

// Grandparent class
class Shape {
    constructor(color) {
        this.color = color;
    }
    
    describe() {
        return `This is a ${this.color} shape`;
    }
}

// Parent class
class Rectangle extends Shape {
    constructor(color, width, height) {
        super(color);
        this.width = width;
        this.height = height;
    }
    
    area() {
        return this.width * this.height;
    }
    
    describe() {
        return `${super.describe()} with area ${this.area()}`;
    }
}

// Child class
class Square extends Rectangle {
    constructor(color, side) {
        super(color, side, side);  // Square has equal width and height
        this.side = side;
    }
    
    describe() {
        return `${super.describe()} (it's a square!)`;
    }
    
    perimeter() {
        return this.side * 4;
    }
}

const mySquare = new Square("red", 5);
console.log(mySquare.describe());
console.log(`Perimeter: ${mySquare.perimeter()}`);
console.log(`Area: ${mySquare.area()}`);

Output:

This is a red shape with area 25 (it's a square!)
Perimeter: 20
Area: 25

🔹 Real-World Example: Game Characters

Let's create a game character system using inheritance:

// Base character class
class Character {
    constructor(name, health, level) {
        this.name = name;
        this.health = health;
        this.maxHealth = health;
        this.level = level;
    }
    
    attack() {
        return `${this.name} attacks for 10 damage`;
    }
    
    heal(amount) {
        this.health = Math.min(this.health + amount, this.maxHealth);
        return `${this.name} healed for ${amount}. Health: ${this.health}/${this.maxHealth}`;
    }
    
    getStatus() {
        return `${this.name} (Level ${this.level}) - Health: ${this.health}/${this.maxHealth}`;
    }
}

// Warrior class
class Warrior extends Character {
    constructor(name, health, level) {
        super(name, health, level);
        this.armor = 5;
    }
    
    attack() {
        return `${this.name} swings sword for 15 damage`;
    }
    
    defend() {
        return `${this.name} raises shield, reducing damage by ${this.armor}`;
    }
}

// Mage class
class Mage extends Character {
    constructor(name, health, level) {
        super(name, health, level);
        this.mana = 50;
    }
    
    attack() {
        if (this.mana >= 10) {
            this.mana -= 10;
            return `${this.name} casts fireball for 20 damage (Mana: ${this.mana})`;
        }
        return `${this.name} is out of mana!`;
    }
    
    castHeal() {
        if (this.mana >= 15) {
            this.mana -= 15;
            this.heal(25);
            return `${this.name} casts heal spell (Mana: ${this.mana})`;
        }
        return `${this.name} doesn't have enough mana to heal`;
    }
}

// Create characters
const warrior = new Warrior("Conan", 100, 5);
const mage = new Mage("Gandalf", 80, 7);

console.log(warrior.getStatus());
console.log(warrior.attack());
console.log(warrior.defend());

console.log(mage.getStatus());
console.log(mage.attack());
console.log(mage.castHeal());

Output:

Conan (Level 5) - Health: 100/100
Conan swings sword for 15 damage
Conan raises shield, reducing damage by 5
Gandalf (Level 7) - Health: 80/80
Gandalf casts fireball for 20 damage (Mana: 40)
Gandalf casts heal spell (Mana: 25)

🔹 Method Overriding vs Method Overloading

Understanding different ways to modify inherited behavior:

class Animal {
    constructor(name, species) {
        this.name = name;
        this.species = species;
    }
    
    makeSound() {
        return `${this.name} makes a generic animal sound`;
    }
    
    move() {
        return `${this.name} moves around`;
    }
}

class Bird extends Animal {
    constructor(name, canFly = true) {
        super(name, "Bird");
        this.canFly = canFly;
    }
    
    // Method Overriding - completely replace parent method
    makeSound() {
        return `${this.name} chirps and tweets`;
    }
    
    // Method Overriding with super - extend parent method
    move() {
        const baseMove = super.move();
        if (this.canFly) {
            return `${baseMove} and flies through the sky`;
        }
        return `${baseMove} by walking and hopping`;
    }
    
    // New method - only available in Bird class
    buildNest() {
        return `${this.name} is building a nest`;
    }
}

class Penguin extends Bird {
    constructor(name) {
        super(name, false);  // Penguins can't fly
    }
    
    // Override Bird's makeSound
    makeSound() {
        return `${this.name} makes penguin sounds`;
    }
    
    // New method specific to penguins
    swim() {
        return `${this.name} swims gracefully underwater`;
    }
}

const robin = new Bird("Robin");
const penguin = new Penguin("Pingu");

console.log(robin.makeSound());
console.log(robin.move());
console.log(robin.buildNest());

console.log(penguin.makeSound());
console.log(penguin.move());
console.log(penguin.swim());

Output:

Robin chirps and tweets
Robin moves around and flies through the sky
Robin is building a nest
Pingu makes penguin sounds
Pingu moves around by walking and hopping
Pingu swims gracefully underwater

🧠 Test Your Knowledge

Which keyword is used to create inheritance in JavaScript?