Inheritance and Polymorphism
Objectives
By the end of this chapter, you should be able to:
- Build a subclass with
extendsthat reuses a base class’s constructor and methods - Call the parent constructor with
super() - Override a method in a subclass
- Explain polymorphism: the same method call producing different behavior depending on the actual object’s class
💡 Why this matters: Real programs often have several kinds of “the same basic thing,” like different shapes, different payment types, or different game characters. Inheritance lets you write the shared behavior once. Polymorphism lets you treat all those different kinds the same way in your code, while each one still does its own thing.
Inheritance with extends
A base class holds behavior shared by everything built on top of it.
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return `${this.name} makes a sound.`;
}
}
A subclass reuses Animal with extends, and can override any method it needs to behave differently.
class Dog extends Animal {
speak() {
return `${this.name} barks.`;
}
}
const rex = new Dog("Rex");
console.log(rex.speak());
// Rex barks.
console.log(rex instanceof Animal);
// true
Dog never redefined a constructor, so it automatically uses Animal’s. rex still gets a name, and rex instanceof Animal is true because a Dog is still an Animal, just a more specific one.
super(): Calling the Parent Constructor
If a subclass needs its own constructor, for extra properties, it must call super() first, which runs the parent class’s constructor.
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
speak() {
return `${this.name} barks.`;
}
}
const rex = new Dog("Rex", "Labrador");
console.log(rex.speak());
// Rex barks.
console.log(rex.name, rex.breed);
// Rex Labrador
console.log(rex instanceof Dog, rex instanceof Animal);
// true true
super(name) calls Animal’s constructor with name, which sets this.name. Only after that can Dog’s constructor safely set this.breed. JavaScript requires super() to run before you touch this in a subclass constructor.
Polymorphism: Same Call, Different Behavior
Polymorphism means calling the same method name on different objects and getting behavior specific to each object’s actual class. Add a second subclass:
class Cat extends Animal {
speak() {
return `${this.name} meows.`;
}
}
const animals = [new Dog("Rex"), new Cat("Whiskers"), new Animal("Generic")];
for (const animal of animals) {
console.log(animal.speak());
}
// Rex barks.
// Whiskers meows.
// Generic makes a sound.
The loop calls animal.speak() the exact same way for every item, it never checks whether animal is a Dog, a Cat, or a plain Animal. Each object still runs its own version of speak(), because that’s the version living on its own class’s prototype (from lesson 1 and 2). The calling code stays simple, while each object handles its own behavior.
Try It
- Write a base class
Shapewith a methoddescribe()that returns"A shape". Write subclassesCircleandSquarethat overridedescribe()to return something specific to each. - Give
Circlea constructor that takesradiusand callssuper(), and giveSquarea constructor that takessideand callssuper(). - Put one
Circleand oneSquarein an array, loop over it, and calldescribe()on each. Confirm the output is different for each even though the method call looks identical. - Add a third subclass and confirm the loop still works without changing the loop itself.
Recap
extendsbuilds a subclass on top of a base class, reusing its constructor and methods unless they’re overridden.super()calls the parent class’s constructor, and must run before a subclass constructor usesthis.- Overriding a method in a subclass replaces the base class’s version for objects of that subclass.
- Polymorphism means the same method call behaves differently depending on the object’s actual class, which lets calling code stay simple while each class handles its own details.
Next lesson: exercises that combine classes, constructors, static methods, encapsulation, inheritance, and polymorphism.