Skip to content
Theory · Classes and prototypes · lesson 5 of 12

Inheritance

Back to the furniture factory. It now needs a rocking chair. There is no point drawing it from scratch: it is the same chair, only with rockers. The engineer copies the finished chair blueprint and adds one difference to the copy.

Classes work the same way. A dog and a cat have something in common: a name and a greeting. If you set up two separate classes for them, you would have to write the same code twice. Instead, a class can take properties and methods from another class:

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

    greet() {
        return `I am ${this.name}`;
    }
}

class Dog extends Animal {}

const rex = new Dog("Rex");
console.log(rex.greet()); // I am Rex
console.log(rex instanceof Dog, rex instanceof Animal); // true true

The body of the class Dog is empty, and yet rex.greet() prints I am Rex. Let's work out where the dog gets its name and its greeting.

extends

The declaration class Dog extends Animal creates a subclass Dog of the class Animal. Animal is its parent class, or parent. This is inheritance: an instance of Dog gets every method of Animal, although the body of Dog has none of them.

What happens in the example:

  1. Dog has no constructor of its own, so new Dog("Rex") calls the parent's constructor, and that writes name.
  2. rex.greet() finds the greet method in the parent and prints I am Rex.

The line can go on: class Puppy extends Dog {} gets the methods of both Dog and Animal.

Here the blueprint analogy stops working. The engineer copies the blueprint onto a new sheet, while Dog copies nothing from Animal: its instances find the parent's methods anew on every call.

instanceof

The instanceof operator checks whether an object was created by this class or by its subclass. rex is an instance of both Dog and Animal, so both expressions give true. A rocking chair is still a chair too: you can sit on it.

Your own constructor and super

If a subclass needs properties of its own, it declares its own constructor. The first thing that constructor does is call super(…), the parent's constructor with the arguments it needs. Only after that can it use this. If a subclass declares a constructor, the call to super(…) in it is required:

JavaScript
class Cat extends Animal {
    constructor(name, lives) {
        super(name);
        this.lives = lives;
    }

    greet() {
        return `${super.greet()}, ${this.lives} lives`;
    }
}

const tom = new Cat("Tom", 9);
console.log(tom.greet()); // I am Tom, 9 lives
console.log(rex.greet()); // I am Rex

The Cat constructor works in two steps:

  1. super(name) runs the Animal constructor, and that writes name into the object.
  2. this.lives = lives adds a property the parent does not have.

Overriding a method

Cat has its own greet method, and for cats it replaces the Animal method of the same name. This is an override. The call tom.greet() runs the Cat version, while instances of Animal and Dog still call the greet of Animal.

Inside the overriding method, super.greet() calls the parent's version. Here it returns "I am Tom", and the Cat method adds the number of lives to it. A call to this.greet() in that place would run the Cat method again. The method would call itself without end, and the program would stop with a RangeError.

The hardest part of this theme is keeping track of the order in which the parent's and the subclass's code run. Press "Next step" and watch the pile of calls: first super(name) goes into the Animal constructor, then super.greet() goes into its method.

Livenew Cat and super step by stepstep 1 of 9 · frames: 1

new Cat("Tom", 9) starts the Cat constructor.

Each frame is an unfinished call; the top one is running now

Interviewers like to ask why super(…) is called before this. The answer: until super(…) is called, the object does not exist yet; the parent's constructor creates it.

Common mistake

The subclass constructor used this before calling super:

JavaScript
class Bird extends Animal {
    constructor(name) {
        this.wings = 2; // ✗
        super(name);
    }
}

const kesha = new Bird("Kesha");
TEXT
ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor

This is the Node.js and Chrome message. The error appears when an object is created with new, not when the class is declared. Until super is called, the object does not exist yet: the parent's constructor creates it. That is why super(name) goes on the first line, and properties are written after it.

Key points

  • class B extends A makes B a subclass: its instances get the methods of A, and instanceof A gives true for them.
  • A subclass's own constructor calls super(…) before any use of this.
  • A method with the same name in a subclass overrides the parent's method.
  • super.method() calls the parent's version, while this.method() calls its own again.

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way