JavaScript 35 🧬 Polymorphism
Polymorphism means “many forms” — it’s the ability of different classes to respond to the same method call in their own way. It’s one of the four pillars of object-oriented programming (along with encapsulation, inheritance, and abstraction).
A Quick Look at the Example
class Animal {
speak() {
return "Some generic animal sound";
}
}
class Dog extends Animal {
speak() {
return "Woof!";
}
}
class Cat extends Animal {
speak() {
return "Meow!";
}
}
class Cow extends Animal {
speak() {
return "Moo!";
}
}
// Same method call — different behavior
const animals = [new Dog(), new Cat(), new Cow()];
animals.forEach(animal => {
console.log(animal.speak());
});
// "Woof!"
// "Meow!"
// "Moo!"
Key idea: animal.speak() looks identical for every animal, but produces a different result depending on the actual class of the object.
What is Polymorphism?
Polymorphism allows objects of different classes to be treated as objects of a common parent class, while each retains its own behavior.
Key Concepts
| Concept | Description |
|---|---|
| Same interface | All objects share the same method names |
| Different behavior | Each class implements the method differently |
| Runtime decision | Which method runs is decided when the code runs |
| Inheritance-based | Polymorphism typically uses inheritance |
Polymorphism Through Inheritance
The most common form of polymorphism in JavaScript is method overriding with inheritance.
class Shape {
area() {
return 0;
}
describe() {
return `A shape with area ${this.area()}`;
}
}
class Circle extends Shape {
constructor(radius) {
super();
this.radius = radius;
}
area() {
return Math.PI * this.radius ** 2;
}
}
class Rectangle extends Shape {
constructor(w, h) {
super();
this.w = w;
this.h = h;
}
area() {
return this.w * this.h;
}
}
class Triangle extends Shape {
constructor(base, height) {
super();
this.base = base;
this.height = height;
}
area() {
return (this.base * this.height) / 2;
}
}
// Polymorphic behavior
const shapes = [
new Circle(5),
new Rectangle(4, 6),
new Triangle(3, 8)
];
shapes.forEach(shape => {
console.log(shape.describe());
});
// "A shape with area 78.53981633974483"
// "A shape with area 24"
// "A shape with area 12"
What’s happening:
shape.describe()callsthis.area()this.area()resolves to the actual class’s method- The same call produces different behavior
How Method Resolution Works
When you call a method on an object, JavaScript looks up the prototype chain:
shapes[0].area()
│
├── Is area on the Circle instance? → No
├── Is area on Circle.prototype? → Yes! ✅ Use this
└── (Stops here — doesn't check Shape)
Visual:
Circle instance
│
└── [[Prototype]] → Circle.prototype
├── area() ← Found here for Circle
│
└── [[Prototype]] → Shape.prototype
├── area() ← Base (overridden)
└── describe()
Polymorphism in Action — Real Examples
Example 1: Payment Methods
class PaymentMethod {
pay(amount) {
throw new Error("pay() must be implemented");
}
}
class CreditCard extends PaymentMethod {
constructor(number) {
super();
this.number = number;
}
pay(amount) {
return `Paid $${amount} with credit card ending in ${this.number.slice(-4)}`;
}
}
class PayPal extends PaymentMethod {
constructor(email) {
super();
this.email = email;
}
pay(amount) {
return `Paid $${amount} via PayPal (${this.email})`;
}
}
class Crypto extends PaymentMethod {
constructor(wallet) {
super();
this.wallet = wallet;
}
pay(amount) {
return `Paid $${amount} in crypto to wallet ${this.wallet.slice(0, 8)}...`;
}
}
// Polymorphic usage
function processPayment(paymentMethod, amount) {
return paymentMethod.pay(amount);
}
const methods = [
new CreditCard("4111-1111-1111-1234"),
new PayPal("alice@example.com"),
new Crypto("0xABC123DEF456")
];
methods.forEach(m => console.log(processPayment(m, 99.99)));
// "Paid $99.99 with credit card ending in 1234"
// "Paid $99.99 via PayPal (alice@example.com)"
// "Paid $99.99 in crypto to wallet 0xABC123..."
Why this is powerful:
processPayment()doesn’t need to know the exact class- It just calls
.pay()on whatever object it receives - Adding a new payment method requires no changes to
processPayment()
Example 2: Notification System
class Notification {
send(message) {
throw new Error("send() must be implemented");
}
}
class EmailNotification extends Notification {
constructor(email) {
super();
this.email = email;
}
send(message) {
return `📧 Email to ${this.email}: ${message}`;
}
}
class SMSNotification extends Notification {
constructor(phone) {
super();
this.phone = phone;
}
send(message) {
return `📱 SMS to ${this.phone}: ${message}`;
}
}
class PushNotification extends Notification {
constructor(deviceId) {
super();
this.deviceId = deviceId;
}
send(message) {
return `🔔 Push to device ${this.deviceId}: ${message}`;
}
}
// Polymorphic dispatch
function notifyAll(notifiers, message) {
return notifiers.map(n => n.send(message));
}
const notifiers = [
new EmailNotification("alice@example.com"),
new SMSNotification("+1-555-1234"),
new PushNotification("device-abc")
];
const results = notifyAll(notifiers, "Server restarted");
results.forEach(r => console.log(r));
// "📧 Email to alice@example.com: Server restarted"
// "📱 SMS to +1-555-1234: Server restarted"
// "🔔 Push to device device-abc: Server restarted"
Example 3: Game Characters
class Character {
constructor(name, hp) {
this.name = name;
this.hp = hp;
}
attack() {
return `${this.name} attacks for 10 damage.`;
}
takeDamage(amount) {
this.hp -= amount;
return `${this.name} takes ${amount} damage. HP: ${this.hp}`;
}
describe() {
return `${this.name} (HP: ${this.hp})`;
}
}
class Warrior extends Character {
constructor(name) {
super(name, 150);
this.rage = 0;
}
attack() {
this.rage += 10;
return `${this.name} swings a sword for 15 damage! Rage: ${this.rage}`;
}
}
class Mage extends Character {
constructor(name) {
super(name, 80);
this.mana = 100;
}
attack() {
if (this.mana >= 20) {
this.mana -= 20;
return `${this.name} casts fireball for 30 damage! Mana: ${this.mana}`;
}
return `${this.name} is out of mana!`;
}
}
class Archer extends Character {
constructor(name) {
super(name, 100);
this.arrows = 10;
}
attack() {
if (this.arrows > 0) {
this.arrows--;
return `${this.name} shoots an arrow for 20 damage! Arrows: ${this.arrows}`;
}
return `${this.name} is out of arrows!`;
}
}
// Polymorphic combat
const party = [
new Warrior("Conan"),
new Mage("Merlin"),
new Archer("Robin")
];
function battle(characters) {
return characters.map(c => c.attack());
}
console.log(battle(party));
// [
// "Conan swings a sword for 15 damage! Rage: 10",
// "Merlin casts fireball for 30 damage! Mana: 80",
// "Robin shoots an arrow for 20 damage! Arrows: 9"
// ]
Example 4: File Readers
class FileReader {
read() {
throw new Error("read() must be implemented");
}
}
class TextReader extends FileReader {
constructor(path) {
super();
this.path = path;
}
read() {
return `Reading text from ${this.path}...`;
}
}
class JSONReader extends FileReader {
constructor(path) {
super();
this.path = path;
}
read() {
return `Parsing JSON from ${this.path}...`;
}
}
class CSVReader extends FileReader {
constructor(path) {
super();
this.path = path;
}
read() {
return `Parsing CSV from ${this.path}...`;
}
}
// Factory + polymorphism
function createReader(filePath) {
if (filePath.endsWith('.txt')) return new TextReader(filePath);
if (filePath.endsWith('.json')) return new JSONReader(filePath);
if (filePath.endsWith('.csv')) return new CSVReader(filePath);
throw new Error("Unsupported file type");
}
const files = ['notes.txt', 'data.json', 'report.csv'];
files.forEach(file => {
const reader = createReader(file);
console.log(reader.read());
});
// "Reading text from notes.txt..."
// "Parsing JSON from data.json..."
// "Parsing CSV from report.csv..."
Types of Polymorphism
| Type | Description | Example |
|---|---|---|
| Method Overriding | Subclass replaces parent method | Dog.speak() overrides Animal.speak() |
| Method Overloading | Same name, different parameters | Not directly supported in JS |
| Duck Typing | If it walks like a duck… | Any object with .speak() works |
| Parametric | Generic functions | map(), filter() work on any type |
Duck Typing (Dynamic Polymorphism)
In JavaScript, polymorphism often works through duck typing — “if it walks like a duck and quacks like a duck, it’s a duck.”
// No inheritance needed!
class Robot {
speak() {
return "Beep boop!";
}
}
class Human {
speak() {
return "Hello!";
}
}
class Alien {
speak() {
return "Zorp glorp!";
}
}
// Same function works with any object that has speak()
function makeItSpeak(thing) {
return thing.speak();
}
const beings = [new Robot(), new Human(), new Alien()];
beings.forEach(b => console.log(makeItSpeak(b)));
// "Beep boop!"
// "Hello!"
// "Zorp glorp!"
Note: Robot, Human, and Alien don’t share a parent — but they all have a speak() method, so the function works with all of them.
Polymorphism vs Inheritance
| Aspect | Inheritance | Polymorphism |
|---|---|---|
| Purpose | Reuse code | Different behavior via same interface |
| Relationship | “is-a” (Dog is an Animal) | “responds-to” (all can speak()) |
| Mechanism | extends keyword | Method overriding, duck typing |
| Focus | Code sharing | Behavior variation |
Inheritance enables polymorphism, but polymorphism is about behavior, not just structure.
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Polymorphism</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background: #f8f9fa;
color: #333;
line-height: 1.6;
}
h1 { color: #007bff; border-bottom: 3px solid #007bff; padding-bottom: 10px; }
h2 { color: #28a745; border-left: 4px solid #28a745; padding-left: 15px; margin-top: 30px; }
.demo-box {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin: 15px 0;
}
pre {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.8;
}
.keyword { color: #569cd6; }
.string { color: #ce9178; }
.number { color: #b5cea8; }
.function { color: #dcdcaa; }
.comment { color: #6a9955; }
.boolean { color: #569cd6; }
.class-name { color: #4ec9b0; }
#output {
background: #e9ecef;
padding: 15px;
border-radius: 8px;
margin-top: 15px;
min-height: 40px;
font-family: 'Courier New', monospace;
font-size: 0.85rem;
border-left: 4px solid #007bff;
white-space: pre-wrap;
}
table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
th, td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
th { background: #007bff; color: white; }
tr:nth-child(even) { background: #f8f9fa; }
.btn {
padding: 10px 20px;
background: #007bff;
color: white;
border: none;
border-radius: 6px;
font-size: 1em;
font-weight: bold;
cursor: pointer;
margin: 5px;
transition: all 0.3s;
}
.btn:hover {
background: #0056b3;
transform: translateY(-2px);
}
.btn-success { background: #28a745; }
.btn-success:hover { background: #1e7e34; }
.hierarchy {
font-family: 'Courier New', monospace;
font-size: 0.85rem;
background: #1e1e1e;
color: #d4d4d4;
padding: 20px;
border-radius: 8px;
margin: 15px 0;
white-space: pre;
overflow-x: auto;
line-height: 1.8;
}
.class-display {
font-family: 'Courier New', monospace;
font-size: 1.05em;
background: #f8f9fa;
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border-left: 4px solid #007bff;
white-space: pre-wrap;
line-height: 1.8;
}
</style>
</head>
<body>
<h1>Polymorphism</h1>
<div class="demo-box">
<h2>1. Basic Polymorphism</h2>
<pre>
<span class="keyword">class</span> <span class="class-name">Animal</span> {
<span class="function">speak</span>() {
<span class="keyword">return</span> <span class="string">"Some generic animal sound"</span>;
}
}
<span class="keyword">class</span> <span class="class-name">Dog</span> <span class="keyword">extends</span> <span class="class-name">Animal</span> {
<span class="function">speak</span>() { <span class="keyword">return</span> <span class="string">"Woof!"</span>; }
}
<span class="keyword">class</span> <span class="class-name">Cat</span> <span class="keyword">extends</span> <span class="class-name">Animal</span> {
<span class="function">speak</span>() { <span class="keyword">return</span> <span class="string">"Meow!"</span>; }
}
<span class="keyword">const</span> animals = [<span class="keyword">new</span> <span class="class-name">Dog</span>(), <span class="keyword">new</span> <span class="class-name">Cat</span>()];
animals.<span class="function">forEach</span>(a => console.log(a.<span class="function">speak</span>()));
<span class="comment">// "Woof!"</span>
<span class="comment">// "Meow!"</span>
</pre>
</div>
<div class="demo-box">
<h2>2. Polymorphic Method Resolution</h2>
<div class="hierarchy">
<span style="color: #4ec9b0;">Shape</span> (parent)
├── area() ← base implementation (returns 0)
└── describe() ← calls this.area()
│
├── <span style="color: #4ec9b0;">Circle</span> extends Shape
│ └── area() → OVERRIDES with πr²
│
├── <span style="color: #4ec9b0;">Rectangle</span> extends Shape
│ └── area() → OVERRIDES with w×h
│
└── <span style="color: #4ec9b0;">Triangle</span> extends Shape
└── area() → OVERRIDES with ½bh
<span style="color: #dcdcaa;">shape.describe()</span>
│
└── calls <span style="color: #dcdcaa;">this.area()</span>
│
└── resolves to the ACTUAL class's area()
</div>
</div>
<div class="demo-box">
<h2>3. The Power of Polymorphism</h2>
<pre>
<span class="comment">// Without polymorphism — need if/else for each type</span>
<span class="keyword">function</span> <span class="function">badDescribe</span>(shape) {
<span class="keyword">if</span> (shape <span class="keyword">instanceof</span> <span class="class-name">Circle</span>) {
<span class="keyword">return</span> <span class="string">`Circle area: ${Math.PI * shape.radius ** 2}`</span>;
} <span class="keyword">else if</span> (shape <span class="keyword">instanceof</span> <span class="class-name">Rectangle</span>) {
<span class="keyword">return</span> <span class="string">`Rectangle area: ${shape.w * shape.h}`</span>;
} <span class="keyword">else if</span> (shape <span class="keyword">instanceof</span> <span class="class-name">Triangle</span>) {
<span class="keyword">return</span> <span class="string">`Triangle area: ${shape.base * shape.height / 2}`</span>;
}
<span class="comment">// Adding a new shape requires editing this function!</span>
}
<span class="comment">// With polymorphism — no changes needed for new shapes</span>
<span class="keyword">function</span> <span class="function">goodDescribe</span>(shape) {
<span class="keyword">return</span> shape.<span class="function">describe</span>();
}
</pre>
</div>
<div class="demo-box">
<h2>4. Types of Polymorphism</h2>
<table>
<tr>
<th>Type</th>
<th>Description</th>
<th>In JavaScript</th>
</tr>
<tr>
<td><strong>Method Overriding</strong></td>
<td>Subclass replaces parent method</td>
<td>✅ Supported</td>
</tr>
<tr>
<td><strong>Method Overloading</strong></td>
<td>Same name, different parameters</td>
<td>⚠️ Not directly (use defaults/rest)</td>
</tr>
<tr>
<td><strong>Duck Typing</strong></td>
<td>"If it walks like a duck..."</td>
<td>✅ Natural in JS</td>
</tr>
<tr>
<td><strong>Parametric</strong></td>
<td>Generic functions</td>
<td>✅ Supported (no types)</td>
</tr>
</table>
</div>
<div class="demo-box">
<h2>5. Interactive: Payment System</h2>
<p>Add different payment methods and process a payment.</p>
<div style="margin: 10px 0;">
<button class="btn btn-success" onclick="addCreditCard()">Add Credit Card</button>
<button class="btn btn-success" onclick="addPayPal()">Add PayPal</button>
<button class="btn btn-success" onclick="addCrypto()">Add Crypto</button>
<button class="btn" onclick="processAll()">Process $99.99</button>
<button class="btn" onclick="clearPayments()">Clear</button>
</div>
<div class="class-display" id="paymentDisplay">No payment methods added yet</div>
</div>
<div class="demo-box">
<h2>6. Interactive: Shape Area Calculator</h2>
<div style="margin: 10px 0;">
<button class="btn btn-success" onclick="addCircleShape()">Add Circle</button>
<button class="btn btn-success" onclick="addRectangleShape()">Add Rectangle</button>
<button class="btn btn-success" onclick="addTriangleShape()">Add Triangle</button>
<button class="btn" onclick="showShapeAreas()">Show All Areas</button>
<button class="btn" onclick="clearShapes()">Clear</button>
</div>
<div class="class-display" id="shapeDisplay">No shapes added yet</div>
</div>
<div class="demo-box">
<h2>7. Live Output — All Examples</h2>
<div id="output">Loading...</div>
</div>
<script>
// ============================================
// Polymorphism — Live Demo
// ============================================
let results = [];
// 1. Basic animal polymorphism
results.push('📌 Basic Polymorphism:\n');
class Animal {
speak() {
return "Some generic animal sound";
}
}
class Dog extends Animal {
speak() { return "Woof!"; }
}
class Cat extends Animal {
speak() { return "Meow!"; }
}
class Cow extends Animal {
speak() { return "Moo!"; }
}
const animals = [new Dog(), new Cat(), new Cow()];
animals.forEach(a => {
results.push(' ' + a.constructor.name + '.speak() → "' + a.speak() + '"');
});
results.push('');
// 2. Shape polymorphism
results.push('📌 Shape Polymorphism:\n');
class Shape {
area() { return 0; }
describe() {
return `${this.constructor.name}: area = ${this.area().toFixed(2)}`;
}
}
class Circle extends Shape {
constructor(radius) {
super();
this.radius = radius;
}
area() {
return Math.PI * this.radius ** 2;
}
}
class Rectangle extends Shape {
constructor(w, h) {
super();
this.w = w;
this.h = h;
}
area() {
return this.w * this.h;
}
}
class Triangle extends Shape {
constructor(base, height) {
super();
this.base = base;
this.height = height;
}
area() {
return (this.base * this.height) / 2;
}
}
const shapes = [
new Circle(5),
new Rectangle(4, 6),
new Triangle(3, 8)
];
shapes.forEach(s => {
results.push(' ' + s.describe());
});
results.push('');
// 3. Polymorphic function
results.push('📌 Polymorphic Function (Same Call, Different Results):\n');
function printArea(shape) {
return `Area: ${shape.area().toFixed(2)}`;
}
shapes.forEach(s => {
results.push(' printArea(' + s.constructor.name + ') → "' + printArea(s) + '"');
});
results.push('');
// 4. Payment methods (polymorphism)
results.push('📌 Payment Methods (Polymorphism):\n');
class PaymentMethod {
pay(amount) {
throw new Error("pay() must be implemented");
}
}
class CreditCard extends PaymentMethod {
constructor(number) {
super();
this.number = number;
}
pay(amount) {
return `Paid $${amount} with credit card ending in ${this.number.slice(-4)}`;
}
}
class PayPal extends PaymentMethod {
constructor(email) {
super();
this.email = email;
}
pay(amount) {
return `Paid $${amount} via PayPal (${this.email})`;
}
}
class Crypto extends PaymentMethod {
constructor(wallet) {
super();
this.wallet = wallet;
}
pay(amount) {
return `Paid $${amount} in crypto to wallet ${this.wallet.slice(0, 10)}...`;
}
}
function processPayment(method, amount) {
return method.pay(amount);
}
const methods = [
new CreditCard("4111-1111-1111-1234"),
new PayPal("alice@example.com"),
new Crypto("0xABC123DEF456789")
];
methods.forEach(m => {
results.push(' ' + processPayment(m, 99.99));
});
results.push('');
// 5. Duck typing (polymorphism without inheritance)
results.push('📌 Duck Typing (No Inheritance):\n');
class Robot {
speak() { return "Beep boop!"; }
}
class Human {
speak() { return "Hello!"; }
}
class Alien {
speak() { return "Zorp glorp!"; }
}
function makeItSpeak(thing) {
return thing.speak();
}
const beings = [new Robot(), new Human(), new Alien()];
beings.forEach(b => {
results.push(' ' + b.constructor.name + '.speak() → "' + makeItSpeak(b) + '"');
});
results.push('');
// 6. Polymorphism in combat
results.push('📌 Polymorphic Combat:\n');
class Character {
constructor(name, hp) {
this.name = name;
this.hp = hp;
}
attack() {
return `${this.name} attacks for 10 damage.`;
}
}
class Warrior extends Character {
constructor(name) {
super(name, 150);
this.rage = 0;
}
attack() {
this.rage += 10;
return `${this.name} swings sword for 15 damage! Rage: ${this.rage}`;
}
}
class Mage extends Character {
constructor(name) {
super(name, 80);
this.mana = 100;
}
attack() {
if (this.mana >= 20) {
this.mana -= 20;
return `${this.name} casts fireball for 30 damage! Mana: ${this.mana}`;
}
return `${this.name} is out of mana!`;
}
}
class Archer extends Character {
constructor(name) {
super(name, 100);
this.arrows = 10;
}
attack() {
if (this.arrows > 0) {
this.arrows--;
return `${this.name} shoots arrow for 20 damage! Arrows: ${this.arrows}`;
}
return `${this.name} is out of arrows!`;
}
}
const party = [
new Warrior("Conan"),
new Mage("Merlin"),
new Archer("Robin")
];
party.forEach(c => {
results.push(' ' + c.attack());
});
results.push('');
// 7. Polymorphism with abstract-like base
results.push('📌 Polymorphic File Readers:\n');
class FileReader {
read() {
throw new Error("read() must be implemented");
}
}
class TextReader extends FileReader {
constructor(path) { super(); this.path = path; }
read() { return `Reading text from ${this.path}...`; }
}
class JSONReader extends FileReader {
constructor(path) { super(); this.path = path; }
read() { return `Parsing JSON from ${this.path}...`; }
}
class CSVReader extends FileReader {
constructor(path) { super(); this.path = path; }
read() { return `Parsing CSV from ${this.path}...`; }
}
function createReader(filePath) {
if (filePath.endsWith('.txt')) return new TextReader(filePath);
if (filePath.endsWith('.json')) return new JSONReader(filePath);
if (filePath.endsWith('.csv')) return new CSVReader(filePath);
throw new Error("Unsupported file type");
}
const files = ['notes.txt', 'data.json', 'report.csv'];
files.forEach(file => {
const reader = createReader(file);
results.push(' ' + reader.read());
});
results.push('');
// 8. Polymorphism in reduce
results.push('📌 Polymorphism with Array Methods:\n');
const totalArea = shapes.reduce((sum, shape) => {
return sum + shape.area();
}, 0);
results.push(' Total area of all shapes: ' + totalArea.toFixed(2));
results.push(' → reduce() calls area() polymorphically on each shape');
document.getElementById('output').textContent = results.join('\n');
// ============================================
// Interactive: Payment System
// ============================================
// Reuse CreditCard, PayPal, Crypto classes from above
const interactivePayments = [];
function addCreditCard() {
const n = "4111-1111-1111-" + Math.floor(1000 + Math.random() * 9000);
interactivePayments.push(new CreditCard(n));
updatePaymentDisplay('Added Credit Card');
}
function addPayPal() {
const emails = ['alice@example.com', 'bob@test.com', 'carol@mail.com'];
const email = emails[Math.floor(Math.random() * emails.length)];
interactivePayments.push(new PayPal(email));
updatePaymentDisplay('Added PayPal');
}
function addCrypto() {
const wallet = "0x" + Math.random().toString(16).slice(2, 12).toUpperCase();
interactivePayments.push(new Crypto(wallet));
updatePaymentDisplay('Added Crypto');
}
function updatePaymentDisplay(message) {
const display = document.getElementById('paymentDisplay');
let text = message + '\n';
text += '─────────────────────\n';
text += `Total methods: ${interactivePayments.length}\n\n`;
interactivePayments.forEach((m, i) => {
text += `${i + 1}. ${m.constructor.name}\n`;
});
display.textContent = text;
}
function processAll() {
const display = document.getElementById('paymentDisplay');
if (interactivePayments.length === 0) {
display.textContent = 'Add some payment methods first!';
return;
}
let text = '💰 Processing $99.99 with all methods:\n';
text += '─────────────────────\n';
interactivePayments.forEach(m => {
text += processPayment(m, 99.99) + '\n';
});
display.textContent = text;
}
function clearPayments() {
interactivePayments.length = 0;
document.getElementById('paymentDisplay').textContent = 'No payment methods added yet';
}
// ============================================
// Interactive: Shape Area Calculator
// ============================================
const interactiveShapes = [];
function addCircleShape() {
const r = Math.floor(Math.random() * 8) + 2;
interactiveShapes.push(new Circle(r));
updateShapeDisplay(`Added Circle with radius ${r}`);
}
function addRectangleShape() {
const w = Math.floor(Math.random() * 8) + 2;
const h = Math.floor(Math.random() * 8) + 2;
interactiveShapes.push(new Rectangle(w, h));
updateShapeDisplay(`Added Rectangle ${w}×${h}`);
}
function addTriangleShape() {
const b = Math.floor(Math.random() * 8) + 2;
const h = Math.floor(Math.random() * 8) + 2;
interactiveShapes.push(new Triangle(b, h));
updateShapeDisplay(`Added Triangle base ${b}, height ${h}`);
}
function updateShapeDisplay(message) {
const display = document.getElementById('shapeDisplay');
let text = message + '\n';
text += '─────────────────────\n';
text += `Total shapes: ${interactiveShapes.length}\n\n`;
interactiveShapes.forEach((s, i) => {
text += `${i + 1}. ${s.describe()}\n`;
});
display.textContent = text;
}
function showShapeAreas() {
const display = document.getElementById('shapeDisplay');
if (interactiveShapes.length === 0) {
display.textContent = 'Add some shapes first!';
return;
}
let text = '📐 All Shape Areas (polymorphic):\n';
text += '─────────────────────\n';
let total = 0;
interactiveShapes.forEach(s => {
text += `${s.describe()}\n`;
total += s.area();
});
text += '─────────────────────\n';
text += `Total area: ${total.toFixed(2)}`;
display.textContent = text;
}
function clearShapes() {
interactiveShapes.length = 0;
document.getElementById('shapeDisplay').textContent = 'No shapes added yet';
}
</script>
</body>
</html>
Quick Reference
What is Polymorphism?
| Aspect | Description |
|---|---|
| Definition | Many forms — same interface, different behavior |
| Mechanism | Method overriding (via inheritance) or duck typing |
| When | Method resolved at runtime |
| Benefit | Add new types without changing existing code |
Types of Polymorphism
| Type | Description | JavaScript |
|---|---|---|
| Method Overriding | Subclass replaces parent method | ✅ Supported |
| Method Overloading | Same name, different params | ⚠️ Partial (defaults/rest) |
| Duck Typing | Any object with matching method works | ✅ Natural |
| Parametric | Generic functions | ✅ Supported |
Polymorphism vs Inheritance
| Aspect | Inheritance | Polymorphism |
|---|---|---|
| Relationship | “is-a” | “responds-to” |
| Mechanism | extends | Method overriding |
| Focus | Code reuse | Behavior variation |
| Enables | Polymorphism | — |
Best Practices
✅ Do This:
// Program to an interface, not an implementation
function render(shape) {
return shape.draw(); // Works for any shape
}
// Use polymorphism to eliminate if/else chains
shapes.forEach(s => s.area()); // Not: if (s instanceof Circle) ...
// Provide base class with common methods
class Shape {
area() { return 0; } // Default
describe() { return `${this.area()}`; }
}
// Throw meaningful errors for unimplemented methods
class Abstract {
method() {
throw new Error("method() must be implemented");
}
}
// Use duck typing for flexibility
function log(thing) {
if (typeof thing.toString === 'function') {
console.log(thing.toString());
}
}
❌ Don’t Do This:
// Don't use instanceof chains
if (animal instanceof Dog) {
animal.woof();
} else if (animal instanceof Cat) {
animal.meow();
}
// Better: animal.speak()
// Don't duplicate logic across classes
class Dog { speak() { return "Woof"; } }
class Cat { speak() { return "Meow"; } }
// Missing shared base class
// Don't make methods behave differently by type
class Shape {
area() {
if (this instanceof Circle) return Math.PI * this.r ** 2;
if (this instanceof Rectangle) return this.w * this.h;
}
}
// Better: override in each subclass
// Don't forget to call super in overriding methods
class Dog extends Animal {
speak() {
// Missing super.speak() if you wanted to extend
return "Woof";
}
}
Common Pitfalls
| Pitfall | Problem | Solution |
|---|---|---|
instanceof chains | Hard to maintain | Use polymorphism |
| Missing base method | Errors at runtime | Define abstract methods |
Forgetting super | Parent logic lost | Call super.method() if extending |
| Inconsistent signatures | Confusing API | Keep same method names/params |
| Over-engineering | Too many classes | Use duck typing when simple |
Real-World Use Cases
| Use Case | Why Polymorphism Helps |
|---|---|
| Payment systems | Add new payment methods without changing checkout |
| UI components | Different widgets respond to render() |
| Game characters | Different classes have different attack() behavior |
| File parsers | Same read() interface for different formats |
| Notifications | Email, SMS, Push all respond to send() |
| Database adapters | MySQL, PostgreSQL, MongoDB all respond to query() |
| Shape drawing | Circle, Rectangle, Triangle all respond to draw() |
Polymorphism in Design Patterns
| Pattern | How Polymorphism Is Used |
|---|---|
| Strategy | Different algorithms behind same interface |
| Factory | Creates objects with polymorphic behavior |
| Observer | Different observers respond to update() |
| Command | Different commands respond to execute() |
| Template Method | Subclasses override steps of an algorithm |
| Iterator | Different collections respond to next() |
Pro Tip: Polymorphism is what makes object-oriented code flexible and extensible. The key insight is:
“Program to an interface, not an implementation.”
Instead of asking “what type is this?” and branching with if/else, you just call the method and let the object decide how to respond. This means:
- Adding a new type requires no changes to existing code
- Your functions become shorter and cleaner
- Your design is open for extension, closed for modification
Remember: Polymorphism works best when all classes share a common interface (method names). Whether through inheritance or duck typing, the goal is the same — one call, many behaviors.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!