Skip to content
Laviyx Learn

Try

JavaScript course menu

JavaScript Class Inheritance: extends, super and Method Overriding

Learn inheritance in JavaScript classes: extending a class with extends, calling the parent constructor with super(), overriding methods and calling super.method(), instanceof with subclasses, extending built-in classes like Error, and composition as an alternative.

Advanced9 min readUpdated
On this page

Inheritance lets one class build on another. The new class (the subclass or child) gets everything from the existing class (the superclass or parent) and can add or change behaviour.

extends#

JavaScript
class Account {
  constructor(owner, balance = 0) {
    this.owner = owner;
    this.balance = balance;
  }

  deposit(amount) {
    this.balance += amount;
    return this.balance;
  }

  describe() {
    return `${this.owner}: ₹${this.balance}`;
  }
}

class SavingsAccount extends Account {
  constructor(owner, balance, interestRate) {
    super(owner, balance);          // run the parent constructor first
    this.interestRate = interestRate;
  }

  addInterest() {
    return this.deposit(this.balance * this.interestRate);
  }
}

const savings = new SavingsAccount("Asha", 10000, 0.04);
savings.deposit(2000);              // inherited
savings.addInterest();              // its own method
console.log(savings.describe());    // inherited
Output
Asha: ₹12480
  • class SavingsAccount extends Account makes SavingsAccount a subclass.
  • In a subclass constructor, you must call super(...) before using this. It runs the parent's constructor.
  • The subclass can use every method of the parent.

Overriding methods#

A subclass can replace a parent method by defining one with the same name. super.methodName() calls the parent's version:

JavaScript
class Account {
  constructor(owner, balance) {
    this.owner = owner;
    this.balance = balance;
  }
  describe() {
    return `${this.owner}: ₹${this.balance}`;
  }
}

class BusinessAccount extends Account {
  constructor(owner, balance, company) {
    super(owner, balance);
    this.company = company;
  }
  describe() {
    return `${super.describe()} (business account for ${this.company})`;
  }
}

const accounts = [new Account("Ravi", 500), new BusinessAccount("Meera", 90000, "Meera Designs")];
for (const account of accounts) {
  console.log(account.describe());
}
Output
Ravi: ₹500
Meera: ₹90000 (business account for Meera Designs)

The loop calls describe() on each account without knowing its exact type, and each object uses its own version. This is polymorphism.

instanceof#

An instance of a subclass is also an instance of its parent:

JavaScript
class Animal {}
class Dog extends Animal {}

const pet = new Dog();
console.log(pet instanceof Dog);
console.log(pet instanceof Animal);
console.log(pet instanceof Array);
console.log(Object.getPrototypeOf(Dog) === Animal);
Output
true
true
false
true

Extending built-in classes#

You can extend built-in classes. The most common case is custom errors:

JavaScript
class HttpError extends Error {
  constructor(status, message) {
    super(message);
    this.name = "HttpError";
    this.status = status;
  }

  get isClientError() {
    return this.status >= 400 && this.status < 500;
  }
}

const error = new HttpError(404, "Page not found");
console.log(error instanceof Error, error.name, error.status, error.isClientError);
Output
true HttpError 404 true

Composition over inheritance#

Inheritance creates a tight link between classes: changes to a parent affect every child, and deep hierarchies (Animal → Mammal → Dog → ServiceDog...) become hard to change. Often it is simpler to combine smaller objects or functions instead (composition):

JavaScript
const canSwim = (obj) => ({ ...obj, swim: () => `${obj.name} swims` });
const canFly = (obj) => ({ ...obj, fly: () => `${obj.name} flies` });

const duck = canFly(canSwim({ name: "Duck" }));
console.log(duck.swim());
console.log(duck.fly());
Output
Duck swims
Duck flies

A useful rule: use inheritance for a clear "is a" relationship with one or two levels (a SavingsAccount is an Account), and composition for "has a" or "can do" relationships.

Practice#

Practice exercise

Create a Shape class with a name and a method describe() that returns "<name> with area <area>" using an area() method. Create Circle (radius) and Rectangle (width, height) subclasses that override area(). Print the description of a circle of radius 2 (rounded area) and a 3 by 4 rectangle.

Show solution
JavaScript
class Shape {
  constructor(name) {
    this.name = name;
  }
  area() {
    return 0;
  }
  describe() {
    return `${this.name} with area ${Math.round(this.area())}`;
  }
}

class Circle extends Shape {
  constructor(radius) {
    super("Circle");
    this.radius = radius;
  }
  area() {
    return Math.PI * this.radius ** 2;
  }
}

class Rectangle extends Shape {
  constructor(width, height) {
    super("Rectangle");
    this.width = width;
    this.height = height;
  }
  area() {
    return this.width * this.height;
  }
}

for (const shape of [new Circle(2), new Rectangle(3, 4)]) {
  console.log(shape.describe());
}
Output
Circle with area 13
Rectangle with area 12

Key takeaway#

Finished this lesson?