6. Polymorphism
Follow along with code examples here!
Table of Contents:
Key Concepts
Polymorphism is the OOP concept where different types of objects can be treated the same way because they share the same interface (the same method names) even though each type implements those methods differently.
Inheritance is not required to achieve polymorphism. However, inheritance guarantees polymorphism and improves the reusability of our code.
Classes Review
So far in this Object-Oriented Programming unit we've learned:
How to encapsulate data and methods into objects.
How to use both factory functions and classes to create interfaces that provide well-defined and controlled access points for interacting with internal data.
How to use inheritance to reuse and extend class interfaces.
How to identify where methods are defined in a prototype chain.
We can see all of this in action in this prototype chain: MarcyStudent → Student → Person
Now, let's introduce the final pillar of object-oriented programming: polymorphism.
Polymorphism
Recall that subclasses are considered instances of their superclasses.
Polymorphism is the OOP concept where different types of objects can be treated the same way because they share the same interface (the same method names) even though each type implements those methods differently.
Polymorphism → poly ("many") + morph ("form") → "having many forms"
For example: A
Personcan take "many forms" — it could be aPerson, aStudent, or aMarcyStudent, but they can all be treated as aPersonbecause they share the same interface.
In this example, ada, alan, and reuben each have the method introduce() despite having different implementations. This means that we can write code like this:
Polymorphism works because of the prototype chain - when we call person.fullName() or person.introduce(), JavaScript walks up the prototype chain to find the most specific implementation.
Question: Take a look at the example below. How would you explain how it demonstrates Polymorphism?
Answer
Both Car and RaceCar implement the drive() method, but with different behaviors. The code cars.forEach((car) => car.drive()) works on all cars regardless of their specific type - we can treat them uniformly because they share the same interface. This is polymorphism: same method name, different implementations, treated the same way in our code.
Why does this matter?
Without polymorphism, a function like printIntroduction would need to confirm that incoming person objects have the fullName() and introduce() methods. Without this check, an error could be thrown for trying to invoke an undefined method.
When to use polymorphism: Polymorphism shines when you're working with collections of related objects (like arrays of different users, or lists of different vehicles) and you want to treat them uniformly. If you find yourself writing lots of if (type === 'X') or instanceof checks, that's a sign polymorphism could simplify your code.
Inheritance Is Not Required For Polymorphism
Look at the code below, does it use inheritance? Does it demonstrate polymorphism?
This code does NOT use inheritance but it DOES demonstrate polymorphism, though a looser form of polymorphism. Since Cat and Dog each define a speak method, instances of these classes can be treated the same as "things that speak".
This kind of loose polymorphism is often referred to as "duck typing" ("if it walks like a duck and it quacks like a duck, then it must be a duck").
Though not required, inheritance guarantees polymorphic behavior through inherited methods. It also reduces the amount of code we have to rewrite.
In this example, if Cat and Dog had a shared Animal parent class, they could inherit its constructor():
Challenge
Create two classes, User and Admin.
A User should have the following properties:
usernamea string provided to the constructorisOnlinewith a default valuefalse
A User should have the following methods:
loginsetsisOnlinetotrueand prints<username> has logged in!logoutsetsisOnlinetofalseand prints<username> has logged out!
An Admin should be a subclass of User. It should also have:
A property
isAdminset totrueA method called
doSecretAdminStuffthat 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.
Finally, create an array containing both the user and admin, and loop through them calling login() on each. Notice how polymorphism lets you treat them uniformly even though one is a User and one is an Admin."
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
Last updated