7. Polymorphism
Table of Contents:
Polymorphism
Polymorphism means "many forms".
Polymorphism is a concept in object-oriented programming where multiple types of objects share "signatures" (they have the same property and method names even if their values/implementations are different).

The impact of polymorphism is that our program can reliably use different types of objects in the same way if they all descend from the same parent class.
class Person {
constructor(name) {
this.name = name;
this.friends = [];
}
makeFriend(friend) {
this.friends.push(friend)
console.log(`Hi ${friend}, my name is ${this.name}, nice to meet you!`);
}
}
class Programmer extends Person {
// the constructor can be inherited too as long as the signature doesn't need changing
// here we totally override the parent method
makeFriend(friend) {
this.friends.push(friend);
console.log('tap tap tap');
}
}
class Musician extends Person {
constructor(name, instrument) {
super(name);
this.instrument = instrument;
}
makeFriend(friend) { // Method Override
super.makeFriend(friend)
console.log(`I can play the ${this.instrument}. Do you know any instruments?`);
}
}
const carmen = new Person("Carmen");
const reuben = new Programmer("Reuben");
const ben = new Musician("Ben", "Piano");
const people = [carmen, reuben, ben];
// Ben, reuben, and carmen are all hanging out.
// Maya enters the room and wants to be friends with everyone!
// Because everyone is a Person, we can do this:
people.forEach(person => person.makeFriend("Maya"));
// Output:
// "Hi Maya, my name is Carmen, nice to meet you!"
// "Hi Maya, my name is Reuben, nice to meet you!"
// "Hi Maya, my name is Ben, nice to meet you!"
// "I can play the Piano. Do you know any instruments?"
This demonstrates polymorphism because ben
, reuben
, and carmen
are all descendants of Person
which we know defines a makeFriend
method. Even though reuben
and carmen
are different subtypes, we can treat them as Person
objects as well.
A Person
can come in "many forms".
Let's look at another example of polymorphism. In this example, we have a Car
class and a RaceCar
subclass.
class Car {
constructor(make, model) {
this.make = make;
this.model = model;
}
drive() {
console.log("Vrooom");
}
}
class RaceCar extends Car {
// the constructor can be inherited too as long as the signature doesn't need changing
drive() { // Method Override
console.log("Vah... Vah...");
super.drive(); // invoke the parent class method
console.log("WHEEEEEEE!!!!");
}
}
const car1 = new Car("Chevy", "Cobalt");
const car2 = new RaceCar("Ferrari", "Portofino");
const car3 = new Car("Tesla", "Model X");
const cars = [car1, car2, car3];
cars.forEach((car) => car.makeSound());
// since they are all Cars, they all have drive, even if they behave differently
Both classes implement a method called drive
but they have their own implementations. The code that calls these methods doesn't care how each class implements drive()
— as long as instances of Car
and RaceCar
have a makeSound
method at all, the code will work.
The subclass RaceCar
uses the Car
drive
method sandwiched between two of its own console.log
statements.
Car
objects can come in many forms (they look the same, but they may behave differently).
Using Classes with Other Classes
Challenge: Refactor the makeFriend
method so that instead of adding a friend's name, it takes in a Person object. When a person is added as a friend, both person objects should have each other as friends.
Challenge: User and Admin
Challenge
Create two classes, User
and Admin
.
A User
should have the following properties:
username
a string provided to the constructorisOnline
with a default valuefalse
A User
should have the following methods:
login
setsisOnline
totrue
and prints<username> has logged in!
logout
setsisOnline
tofalse
and prints<username> has logged out!
An Admin
should be a subclass of User
. It should also have:
A property
isAdmin
set totrue
A method called
doSecretAdminStuff
that just prints a message"Doing secret admin stuff"
.
Then, create a user instance and an admin instance and demonstrate how to use all of their methods.
Summary
Polymorphism ("many forms") occurs when multiple types of objects share "signatures" (they have the same property and method names).
The impact of polymorphism is that our program can reliably use different types of objects in the same way if they all descend from the same parent class.
Method Overriding means that method signatures are the same even if their implementations are different
class Car {
constructor(make, model) {
this.make = make;
this.model = model;
}
drive() {
console.log("Vrooom");
}
}
class RaceCar extends Car {
// the constructor can be inherited too as long as the signature doesn't need changing
drive() { // Method Override
console.log("Vah... Vah...");
super.drive(); // invoke the parent class method
console.log("WHEEEEEEE!!!!");
}
}
const car1 = new Car("Chevy", "Cobalt");
const car2 = new RaceCar("Ferrari", "Portofino");
const car3 = new Car("Tesla", "Model X");
const cars = [car1, car2, car3];
cars.forEach((car) => car.makeSound());
// since they are all Cars, they all have drive, even if they behave differently
Last updated