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:
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 trueThe 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:
Doghas noconstructorof its own, sonew Dog("Rex")calls the parent'sconstructor, and that writesname.rex.greet()finds thegreetmethod in the parent and printsI 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:
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 RexThe Cat constructor works in two steps:
super(name)runs theAnimalconstructor, and that writesnameinto the object.this.lives = livesadds 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.
1class Animal {2 constructor(name) {3 this.name = name;4 }5 greet() {6 return `I am ${this.name}`;7 }8}9 10class Cat extends Animal {11 constructor(name, lives) {12 super(name);13 this.lives = lives;14 }15 greet() {16 const s = super.greet();17 return `${s}, ${this.lives} lives`;18 }19}20 21const tom = new Cat("Tom", 9);22console.log(tom.greet());
new Cat("Tom", 9) starts the Cat constructor.
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:
class Bird extends Animal {
constructor(name) {
this.wings = 2; // ✗
super(name);
}
}
const kesha = new Bird("Kesha");ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructorThis 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 AmakesBa subclass: its instances get the methods ofA, andinstanceof Agivestruefor them.- A subclass's own
constructorcallssuper(…)before any use ofthis. - A method with the same name in a subclass overrides the parent's method.
super.method()calls the parent's version, whilethis.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.