Features of Polymorphism
Polymorphism in JavaScript offers several advantages:
- Code Reusability: Allows methods to be reused across different objects.
- Flexibility: Functions and methods can handle different data types or arguments.
- Scalability: Makes code easier to extend without modifying existing logic.
- Simplified Maintenance: Reduces redundant code, making it easier to update.
Polymorphism with Functions and Objects
JavaScript allows polymorphism with functions and objects by treating different objects in a similar way if they share the same method names.
Example
class Bird {
fly() {
console.log("The bird is flying");
}
}
class Airplane {
fly() {
console.log("The airplane is taking off");
}
}
function takeOff(entity) {
entity.fly();
}
let myBird = new Bird();
let myPlane = new Airplane();
takeOff(myBird);
takeOff(myPlane);

You can also try this code with Online Javascript Compiler
Run Code
Output:
The bird is flying
The airplane is taking off
Explanation:
- Both Bird and Airplane classes have a fly() method.
- The takeOff() function calls fly(), but it works for both objects due to polymorphism.
Polymorphism in Functional Programming
JavaScript supports functional programming, where polymorphism is implemented using higher-order functions and callbacks.
Example
function execute(operation, a, b) {
return operation(a, b);
}
function add(x, y) {
return x + y;
}
function multiply(x, y) {
return x * y;
}
console.log(execute(add, 5, 3));
console.log(execute(multiply, 5, 3));

You can also try this code with Online Javascript Compiler
Run Code
Output:
8
15
Explanation
- The execute function takes another function (operation) as an argument.
- It calls the provided function with two numbers, allowing dynamic execution.
- This demonstrates polymorphism since different functions (add, multiply) work with the same execute() function.
Design Patterns Leveraging Polymorphism
Polymorphism is widely used in design patterns to create scalable and maintainable applications. Some common patterns include:
- Factory Pattern: Creates different types of objects with a common interface.
- Strategy Pattern: Defines a family of algorithms and lets them be interchangeable.
- Decorator Pattern: Adds behavior to objects dynamically without modifying their structure.
Example of Strategy Pattern
class Payment {
process(amount) {
console.log("Processing payment of $" + amount);
}
}
class CreditCardPayment extends Payment {
process(amount) {
console.log("Processing credit card payment of $" + amount);
}
}
class PayPalPayment extends Payment {
process(amount) {
console.log("Processing PayPal payment of $" + amount);
}
}
function makePayment(paymentMethod, amount) {
paymentMethod.process(amount);
}
let creditCard = new CreditCardPayment();
let paypal = new PayPalPayment();
makePayment(creditCard, 100);
makePayment(paypal, 50);

You can also try this code with Online Javascript Compiler
Run Code
Output:
Processing credit card payment of $100
Processing PayPal payment of $50
Explanation
- The base Payment class has a process() method.
- Subclasses (CreditCardPayment, PayPalPayment) override process().
- The makePayment() function works polymorphically with different payment methods.
Types of Polymorphism in JavaScript
Polymorphism in JavaScript can be categorized into three main types: Ad-hoc Polymorphism, Parametric Polymorphism, & Subtype Polymorphism. Each type has its own unique way of handling multiple forms of behavior. Let’s discuss each one in detail.
1. Ad-hoc Polymorphism
Ad-hoc polymorphism refers to functions or methods that behave differently based on the type or number of arguments passed to them. This is also known as function overloading in some programming languages.
In JavaScript, ad-hoc polymorphism is often implemented using conditional logic inside a function to handle different types of inputs.
Example
function add(a, b) {
if (typeof a === "number" && typeof b === "number") {
return a + b;
} else if (typeof a === "string" && typeof b === "string") {
return a.concat(b);
} else {
return "Invalid input types";
}
}
console.log(add(5, 10));
console.log(add("Hello, ", "World!"));
console.log(add(5, "Hello"));

You can also try this code with Online Javascript Compiler
Run Code
Output:
15
Hello, World!
Invalid input types
In this example, the `add` function behaves differently depending on the type of arguments passed. If both arguments are numbers, it performs addition. If both are strings, it concatenates them. Otherwise, it returns an error message.
Note: Ad-hoc polymorphism is useful when you want a single function to handle multiple types of inputs without creating separate functions for each type.
2. Parametric Polymorphism
Parametric polymorphism is a type of polymorphism where a function or method can operate on any data type without needing to know the specific type in advance. This is commonly seen in generic functions or generic programming. In JavaScript, parametric polymorphism is often achieved using template literals or higher-order functions.
The key idea here is that the same code can work with different types of data without requiring any changes.
Example
Let’s create a generic function called `identity` that returns whatever value is passed to it:
function identity(value) {
return value;
}
console.log(identity(10));
console.log(identity("Hello"));
console.log(identity(true));

You can also try this code with Online Javascript Compiler
Run Code
Output:
10
Hello
true
In this example, the `identity` function works with numbers, strings, & booleans. It doesn’t need to know the type of the input in advance. This is a simple example of parametric polymorphism.
Another common example is using arrays in JavaScript. Arrays can store any type of data, & methods like `map`, `filter`, & `reduce` work with any data type.
const numbers = [1, 2, 3, 4];
const strings = ["apple", "banana", "cherry"];
const doubledNumbers = numbers.map((num) => num 2);
const upperCaseStrings = strings.map((str) => str.toUpperCase());
console.log(doubledNumbers);
console.log(upperCaseStrings);
Output:
[2, 4, 6, 8]
["APPLE", "BANANA", "CHERRY"]
Here, the `map` method works with both numbers & strings. It doesn’t care about the type of data in the array. This is another example of parametric polymorphism in action.
Note: Parametric polymorphism is useful when you want to write reusable code that can handle any data type. It reduces redundancy & makes your code more flexible.
3. Subtype Polymorphism
Subtype polymorphism is one of the most common forms of polymorphism in object-oriented programming. It occurs when a subclass or child class provides a specific implementation of a method that is already defined in its parent class. This allows objects of different classes to be treated as objects of a common superclass while still retaining their unique behavior.
In JavaScript, subtype polymorphism is achieved through inheritance. When a child class inherits from a parent class, it can override the parent class’s methods to provide its own implementation.
Example:
Let’s create a parent class called `Shape` & two child classes called `Circle` & `Rectangle`. Each child class will override the `draw` method of the parent class to provide its own implementation.
class Shape {
draw() {
console.log("Drawing a shape");
}
}
class Circle extends Shape {
draw() {
console.log("Drawing a circle");
}
}
class Rectangle extends Shape {
draw() {
console.log("Drawing a rectangle");
}
}
const shapes = [new Shape(), new Circle(), new Rectangle()];
shapes.forEach((shape) => shape.draw());

You can also try this code with Online Javascript Compiler
Run Code
Output:
Drawing a shape
Drawing a circle
Drawing a rectangle
In this example, the `Shape` class has a `draw` method, which is overridden by the `Circle` & `Rectangle` classes. When we call the `draw` method on each object in the `shapes` array, JavaScript automatically calls the correct implementation based on the object’s type.
This is subtype polymorphism in use. Even though all objects in the `shapes` array are treated as instances of the `Shape` class, they retain their unique behavior when the `draw` method is called.
Another Example
Let’s take a real-world analogy. Suppose you have a parent class called `Animal` & child classes like `Dog`, `Cat`, & `Bird`. Each animal makes a different sound, so the `makeSound` method is overridden in each child class.
class Animal {
makeSound() {
console.log("Animal sound");
}
}
class Dog extends Animal {
makeSound() {
console.log("Woof!");
}
}
class Cat extends Animal {
makeSound() {
console.log("Meow!");
}
}
class Bird extends Animal {
makeSound() {
console.log("Chirp!");
}
}
const animals = [new Dog(), new Cat(), new Bird()];
animals.forEach((animal) => animal.makeSound());

You can also try this code with Online Javascript Compiler
Run Code
Output:
Woof!
Meow!
Chirp!
Here, the `makeSound` method behaves differently for each animal, even though all objects are treated as instances of the `Animal` class.
Note: Subtype polymorphism is powerful because it allows you to write code that works with a general type (like `Shape` or `Animal`) while still leveraging the specific behavior of child classes.
Method Overriding
Method overriding occurs when a subclass provides a specific implementation for a method that is already defined in its parent class. This is useful when a child class needs to modify the behavior of an inherited method.
Example
class Animal {
speak() {
console.log("The animal makes a sound");
}
}
class Dog extends Animal {
speak() {
console.log("The dog barks");
}
}
let myPet = new Dog();
myPet.speak();

You can also try this code with Online Javascript Compiler
Run Code
Output:
The dog barks
Explanation
- The Animal class has a speak() method.
- The Dog class extends Animal and overrides the speak() method.
- When speak() is called on a Dog object, it executes the overridden method in the subclass.
Simulating Method Overloading
JavaScript does not support traditional method overloading like other languages (e.g., Java, C++). However, we can simulate it by using conditional statements within a single function to handle different argument types or counts.
Example
function greet(name, age) {
if (age === undefined) {
console.log("Hello, " + name);
} else {
console.log("Hello, " + name + ". You are " + age + " years old.");
}
}
greet("Alice");
greet("Bob", 25);

You can also try this code with Online Javascript Compiler
Run Code
Output:
Hello, Alice
Hello, Bob. You are 25 years old.
Explanation:
- The greet function checks if the age parameter is provided.
- It executes different logic based on the number of arguments passed.
Frequently Asked Questions
What is polymorphism in JavaScript?
Polymorphism in JavaScript allows functions, methods, or objects to behave differently based on their context, enabling code reusability and flexibility.
How does JavaScript support polymorphism?
JavaScript supports polymorphism through method overriding, function parameter handling, and functional programming techniques such as higher-order functions.
Why is polymorphism useful in JavaScript?
Polymorphism makes code more maintainable, reusable, and scalable by allowing different objects or functions to share common interfaces while implementing different behaviors.
Conclusion
In this article, we learned about Polymorphism in JavaScript and how it helps objects share the same method but behave differently. We explored method overriding, prototype-based inheritance, and how JavaScript supports polymorphism. Understanding this concept makes code more flexible and reusable. Using polymorphism, developers can write cleaner and more efficient JavaScript programs.