Inheritance in javascript is a feature that allows a class to extend the properties of other classes.
JS

THE KEYWORD EXTENDS

Let’s assume we have an Animal class:

class Animal {

  constructor(name) {

    this.speed = 0;

    this.name = name;

  }

  run(speed) {

    this.speed = speed;

    alert(`${this.name} runs with speed ${this.speed}.`);

  }

  stop() {

    this.speed = 0;

    alert(`${this.name} stands still.`);

  }

}

let animal = new Animal(“My animal“);

Here we see how to represent the Animal object and the Animal class graphically:

Class Animal

We may want to create another Rabbit class. Since rabbits are animals, the Rabbit class should be based on Animal, having access to all of Animal’s methods, this way Rabbit can take on all the basic behaviors of an Animal. Let’s create a class Rabbit that inherits from Animal:

class Rabbit extends Animal {

  hide() {

    alert(`${this.name} hides!`);

  }

}

let rabbit = new Rabbit(“White Rabbit“);

rabbit.run(5); // White Rabbit runs with speed 5.

rabbit.hide(); // White Rabbit hides!

The object of the Rabbit class has access to both Rabbit methods (for example rabbit.hide()) than to those of Animal (rabbit.run()). Internally, extends adds from Rabbit.prototype a reference [[Prototype]] to Animal.prototype:

Extends

For example, to find the rabbit.run method, the JavaScript engine checks (from bottom to top in the figure):

  1. The rabbit object (does not have run).
  2. Its prototype, which is Rabbit.prototype (owns hide, but not run).
  3. Its prototype, which is (due to extends) Animal.prototype, which owns the run method.

As we recall from Native prototypes, JavaScript itself uses prototype inheritance for embedded objects. For example Date.prototype. [[Prototype]] is Object.prototype. This is why dates have access to the generic methods of an object.

OVERWRITE A METHOD

Let’s move on now and see how to override a method. Basically, all methods that are not defined in class Rabbit are taken “as is” by class Animal. But if we specify a method in Rabbit, like stop() then this will be used:

class Rabbit extends Animal {

  stop() {

    // questo verrà utilizzato per rabbit.stop()

    // piuttosto di stop() dal padre, class Animal

  }

}

However, we normally don’t want to completely replace the inherited method, but rather build on it, modify it slightly or extend its functionality. In our method we perform actions, but at some point we call the inherited method.

The classes provide the “super” keyword for this purpose.

  • super.method (…) to call a method from the parent;
  • super (…) to call the father’s constructor (valid only within our constructor).

For example, let’s make our rabbit hide automatically when it stops:

class Animal {

  constructor(name) {

    this.speed 0;

    this.name name;

  }

  run(speed) {

    this.speed speed;

    alert(`${this.nameruns with speed ${this.speed}.`);

  }

  stop() {

    this.speed = 0;

    alert(`${this.namestands still.`);

  }

}

class Rabbit extends Animal {

  hide() {

    alert(`${this.namehides!`);

  }

  stop() {

    super.stop()// richiama il metodo stop() dal padre

    this.hide();    // and then hide

  }

}

let rabbit new Rabbit(“White Rabbit”);

rabbit.run(5)// White Rabbit runs with speed 5.

rabbit.stop(); // White Rabbit stands still. White Rabbit hides!

Now Rabbit contains the stop method, which calls the method inside super.stop().

OVERWRITE THE CONSTRUCTOR

Overriding a constructor is slightly more complicated. So far, Rabbit hasn’t had its own constructor method. According to the specifications, if a class extends another and does not have its own constructor method, the following “empty” constructor is generated:

class Rabbit extends Animal {

  // generato per classi figlie senza un costruttore proprio

  constructor(…args) {

    super(…args);

  }

}

As we can see, it calls the parent’s constructor, passing it all the arguments. This happens if we don’t create an ad hoc constructor. So let’s add a custom constructor for Rabbit, which will specify, in addition to the name, also the earLength property:

class Animal {

  constructor(name) {

    this.speed = 0;

    this.name = name;

  }

  // …

}

class Rabbit extends Animal {

  constructor(nameearLength) {

    this.speed = 0;

    this.name name;

    this.earLength earLength;

  }

  // …

}

// Non funziona!

let rabbit new Rabbit(“White Rabbit”, 10); // Error: this is not defined.

(Error: “this” is not defined) Oops! We have received an error. Now we cannot create rabbits (rabbits). What went wrong?

The short answer is:

  • Constructors in inheriting classes must call super (…), and must do it (!) Before using this.

But why? What is going on? In fact, this request seems a bit strange. Obviously there is an explanation. Let’s go into the details, so as to understand what actually happens. In JavaScript there is a clear distinction between the “constructor method of a child class” and all the others. In a child class, the constructor is labeled with a special internal property: [[ConstructorKind]]: “derived”.

The difference is:

  • When a normal constructor is executed, it creates an empty object called this and continues to work on that. This is not the case when a child class constructor is executed, as it expects the parent’s constructor to do it for it.

If we are creating the constructor of a child we must necessarily call super, otherwise the object referenced by this would not be created. And we would get an error. To get Rabbit to work we need to call super() before using this:

class Animal {

  constructor(name) {

    this.speed = 0;

    this.name name;

  }

  // …

}

class Rabbit extends Animal {

  constructor(name, earLength) {

    super(name);

    this.earLength earLength;

  }

  // …

}

// finalmente

let rabbit new Rabbit(“White Rabbit”, 10);

alert(rabbit.name); // White Rabbit

alert(rabbit.earLength); // 10

Copy to Clipboard

THE JAVASCRIPT LANGUAGE

THE JAVASCRIPT LANGUAGE

LINK TO THE CODE ON GITHUB

GITHUB