Polymorphism is one of the four fundamental concepts of Object-Oriented Programming (OOP). The word polymorphism means “many forms.” In programming, it allows the same method name or interface to produce different behaviors depending on the object or the values being used.
In JavaScript, polymorphism is commonly achieved through inheritance, method overriding, and shared interfaces. It helps developers write flexible and reusable code that works with different types of objects.
For example, a Dog and a Cat can both have a makeSound() method. When the method is called, the dog barks while the cat meows. The same method name produces different results.
In this tutorial, you will learn what polymorphism is, how it works in JavaScript, and how to implement it using practical examples.
What Is Polymorphism in JavaScript?
Polymorphism is an OOP concept that allows different objects to respond to the same method call in different ways.
Instead of writing separate code for every object type, you can use a common method name and let each object provide its own behavior.
Example of Polymorphism
class Animal {
makeSound() {
console.log("Animal makes a sound");
}
}
class Dog extends Animal {
makeSound() {
console.log("Dog barks");
}
}
class Cat extends Animal {
makeSound() {
console.log("Cat meows");
}
}
const dog = new Dog();
const cat = new Cat();
dog.makeSound();
cat.makeSound();
Output:
Dog barks
Cat meows
In this example:
Animalis the parent class.DogandCatare child classes.- Both child classes define their own
makeSound()method. - The same method name produces different behavior depending on the object.
This is polymorphism through method overriding.
How Polymorphism Works in JavaScript
Polymorphism allows you to write code that works with different objects without needing to know the exact class of every object in advance.
JavaScript determines which method to call based on the object’s prototype chain and the method that is available on that object.
Consider the following example:
class Vehicle {
start() {
console.log("Vehicle is starting");
}
}
class Car extends Vehicle {
start() {
console.log("Car engine started");
}
}
class Bike extends Vehicle {
start() {
console.log("Bike engine started");
}
}
function startVehicle(vehicle) {
vehicle.start();
}
startVehicle(new Car());
startVehicle(new Bike());
Output:
Car engine started
Bike engine started
The startVehicle() function accepts different objects. It calls start() without checking whether the object is a car or a bike.
Each object provides its own implementation of the method.
This makes the function flexible and reduces the need for repeated conditional statements.
Method Overriding in Polymorphism
Method overriding occurs when a child class defines a method with the same name as an inherited method.
When the method is called on a child instance, JavaScript uses the child’s implementation.
Example of Method Overriding
class Employee {
calculateSalary() {
return 20000;
}
}
class Developer extends Employee {
calculateSalary() {
return 40000;
}
}
class Manager extends Employee {
calculateSalary() {
return 60000;
}
}
const developer = new Developer();
const manager = new Manager();
console.log(developer.calculateSalary());
console.log(manager.calculateSalary());
Output:
40000
60000
The Developer and Manager classes override the inherited calculateSalary() method.
Although both objects use the same method name, the returned salary differs according to the class implementation.
Using super() with Method Overriding
Sometimes, a child class needs to extend the behavior of a parent method rather than replace it entirely.
You can use super.methodName() to call the parent implementation.
class Employee {
display() {
console.log("Employee details");
}
}
class Developer extends Employee {
display() {
super.display();
console.log("Developer writes code");
}
}
const developer = new Developer();
developer.display();
Output:
Employee details
Developer writes code
The child method first calls the parent method and then adds its own behavior.
Polymorphism Using a Common Interface
JavaScript does not require unrelated objects to inherit from the same class to use polymorphism.
Objects can support the same method name and be used through a common interface. This is often called duck typing: if an object provides the behavior a function needs, the function can use that object.
Example Without Inheritance
const dog = {
speak() {
console.log("Woof!");
}
};
const cat = {
speak() {
console.log("Meow!");
}
};
const robot = {
speak() {
console.log("Robot speaking");
}
};
function makeItSpeak(object) {
object.speak();
}
makeItSpeak(dog);
makeItSpeak(cat);
makeItSpeak(robot);
Output:
Woof!
Meow!
Robot speaking
The three objects do not need to share a parent class. They only need to provide a compatible speak() method.
This style is useful when working with plugins, reusable functions, and objects from different parts of an application.
Polymorphism Through Method Parameters
Polymorphism can also appear when the same function or operator works with different kinds of values.
JavaScript is dynamically typed, so a function can accept different values and behave according to the operations it performs.
Example with a Function
function display(value) {
console.log(value);
}
display("Hello JavaScript");
display(100);
display(true);
Output:
Hello JavaScript
100
true
The same function accepts a string, a number, and a boolean.
This demonstrates flexible function behavior, although it is not the same as method overriding in class-based OOP.
Example with the Addition Operator
console.log(10 + 20);
console.log("Hello " + "World");
Output:
30
Hello World
The + operator performs numeric addition with numbers and string concatenation when strings are involved. This is an example of operator overloading-like behavior built into JavaScript, rather than user-defined operator overloading.
Practical Example of Polymorphism
Imagine an online payment application that supports different payment methods, such as credit cards and digital wallets.
Each payment method can provide its own pay() implementation. The application can then process payments through a shared interface.
class CreditCardPayment {
pay(amount) {
console.log(`Paid ₹${amount} using a credit card`);
}
}
class WalletPayment {
pay(amount) {
console.log(`Paid ₹${amount} using a digital wallet`);
}
}
class BankTransferPayment {
pay(amount) {
console.log(`Paid ₹${amount} using a bank transfer`);
}
}
function processPayment(paymentMethod, amount) {
paymentMethod.pay(amount);
}
processPayment(new CreditCardPayment(), 1000);
processPayment(new WalletPayment(), 500);
processPayment(new BankTransferPayment(), 2000);
Output:
Paid ₹1000 using a credit card
Paid ₹500 using a digital wallet
Paid ₹2000 using a bank transfer
How This Example Works
- Each payment class defines a
pay()method. - Each method handles the payment differently.
processPayment()accepts any object that supportspay().- The function does not need separate logic for each payment type.
In a real payment system, each implementation would also need appropriate validation, security checks, and integration with the relevant payment service.
Types of Polymorphism in JavaScript
Polymorphism can be discussed in several forms.
| Type | Description | JavaScript example |
|---|---|---|
| Subtype polymorphism | Different related objects share a common interface | A Dog and Cat override makeSound() |
| Duck typing | Objects are used based on the behavior they provide | Different objects implement speak() |
| Parametric polymorphism | Code works across different types under a general abstraction | Generic functions in TypeScript |
| Ad hoc polymorphism | The same operation has different behavior for different inputs | The built-in + operator |
JavaScript supports subtype polymorphism and duck typing directly. It does not provide traditional user-defined function or operator overloading in the same way as languages such as Java or C++.
TypeScript adds static type-system features that can express generic, reusable functions more explicitly.
Polymorphism vs Inheritance
Inheritance and polymorphism are related OOP concepts, but they serve different purposes.
| Inheritance | Polymorphism |
|---|---|
| Allows a class to inherit functionality from another class | Allows different objects to provide different behavior through a common interface |
Commonly uses extends and super | Commonly uses method overriding or shared method names |
| Focuses on relationships between classes | Focuses on interchangeable behavior |
Example: Dog extends Animal | Example: Dog and Cat implement makeSound() differently |
Inheritance can help implement polymorphism, but polymorphism can also work without inheritance through compatible object interfaces.
Advantages of Polymorphism in JavaScript
Polymorphism provides several benefits when designing applications.
- Code reusability: One function can work with different compatible objects.
- Flexibility: New implementations can be added without rewriting the calling code.
- Maintainability: Shared interfaces make application logic easier to organize.
- Extensibility: New payment methods, notification channels, or file processors can implement existing behavior.
- Reduced conditional logic: Functions can delegate behavior to the object instead of checking its type repeatedly.
Polymorphism works best when objects share a clear and meaningful interface.
Common Mistakes When Using Polymorphism
Using Different Method Names
If a function expects makeSound(), an object that only defines speak() will not satisfy that interface unless you adapt the function or object.
Use consistent method names when objects are expected to work interchangeably.
Assuming Inheritance Is Always Required
JavaScript objects can support the same interface without inheriting from a common parent class.
Use inheritance when it represents a genuine relationship. Use compatible object interfaces or composition when they better fit the design.
Confusing Overloading with Overriding
Method overriding replaces inherited behavior in a child class. JavaScript does not support traditional method overloading based solely on different parameter lists in the same way as some statically typed languages.
If you define multiple class methods with the same name, the later definition replaces the earlier one.
Ignoring Missing Methods
If a function calls object.pay() but the supplied object does not have a callable pay method, the program can throw a TypeError.
Ensure that objects satisfy the interface expected by the function, or validate the object before calling its method.
FAQs About JavaScript Polymorphism
What is polymorphism in JavaScript?
Polymorphism allows different objects to respond to the same method call in different ways. It helps create flexible and reusable code.
How do you achieve polymorphism in JavaScript?
You can achieve polymorphism through method overriding in class inheritance or by using different objects that implement the same method interface.
What is method overriding?
Method overriding occurs when a child class defines its own implementation of an inherited method.
Can polymorphism work without inheritance in JavaScript?
Yes. JavaScript supports duck typing, which allows a function to work with unrelated objects that provide the required methods.
What is a real-world example of polymorphism?
A payment application can use the same pay() method for credit cards, digital wallets, and bank transfers, with each payment method implementing its own behavior.
What is the difference between polymorphism and encapsulation?
Polymorphism focuses on interchangeable behavior through a shared interface. Encapsulation focuses on grouping data and behavior while controlling access to an object’s internal state.
Does JavaScript support method overloading?
JavaScript does not support traditional method overloading based solely on parameter types or counts. Developers can implement similar behavior using optional parameters, rest parameters, or conditional logic.
Conclusion
Polymorphism is an important OOP concept that allows different JavaScript objects to respond to the same method call in different ways.
You can implement it through method overriding, inheritance, or shared object interfaces. It helps reduce repeated code, improve flexibility, and make applications easier to extend.
By understanding polymorphism alongside inheritance and encapsulation, you can build more modular and maintainable JavaScript applications.