JavaScript 34 🧬 Class Method Override

Method overriding is when a subclass provides its own implementation of a method that already exists in its parent class. The subclass’s method “takes over” — when called on an instance of the subclass, the new method runs instead of the parent’s.
A Quick Look at the Example
class Animal {
speak() {
return "Some generic animal sound";
}
}
class Dog extends Animal {
// Override parent's speak()
speak() {
return "Woof!";
}
}
class Cat extends Animal {
// Override parent's speak()
speak() {
return "Meow!";
}
}
const animal = new Animal();
const dog = new Dog();
const cat = new Cat();
console.log(animal.speak()); // "Some generic animal sound"
console.log(dog.speak()); // "Woof!" (overridden)
console.log(cat.speak()); // "Meow!" (overridden)
a. What is Method Overriding?
Method overriding allows a subclass to replace or customize a method inherited from its parent class.
Key Points
| Aspect | Description |
|---|---|
| Definition | Subclass provides its own version of a parent’s method |
| Same name | The overriding method has the same name as the parent’s |
| Same signature | Same parameters (ideally) |
| Runtime decision | Which method runs is determined by the instance’s actual class |
Why Override Methods?
| Reason | Description |
|---|---|
| Specialization | The subclass needs different behavior |
| Polymorphism | Same method name, different implementations |
| Customization | Adapt parent behavior to specific needs |
| Extensibility | Add to parent behavior (via super.method()) |
b. Basic Method Overriding
class Shape {
area() {
return 0;
}
describe() {
return `A shape with area ${this.area()}`;
}
}
class Circle extends Shape {
constructor(radius) {
super();
this.radius = radius;
}
// Override area()
area() {
return Math.PI * this.radius ** 2;
}
}
class Rectangle extends Shape {
constructor(width, height) {
super();
this.width = width;
this.height = height;
}
// Override area()
area() {
return this.width * this.height;
}
}
const circle = new Circle(5);
const rect = new Rectangle(4, 6);
console.log(circle.describe()); // "A shape with area 78.53981633974483"
console.log(rect.describe()); // "A shape with area 24"
What’s happening:
Shape.describe()callsthis.area()this.area()resolves to the subclass’sarea()— polymorphism!
Visual:
circle.describe()
│
▼
Shape.describe() {
return `... ${this.area()}`; ← this.area()
}
│
▼
Circle.area() { ← Resolved to Circle's method!
return Math.PI * this.radius ** 2;
}
c. Overriding vs Extending
There are two ways to override a method:
| Approach | Behavior | Uses super? |
|---|---|---|
| Replace | Completely replaces parent method | ❌ No |
| Extend | Calls parent + adds behavior | ✅ Yes |
Replace (Complete Override)
class Animal {
speak() {
return "Animal sound";
}
}
class Dog extends Animal {
speak() {
return "Woof!"; // Completely replaces parent
}
}
console.log(new Dog().speak()); // "Woof!"
Extend (Call Parent + Add)
class Animal {
speak() {
return "Animal sound";
}
}
class Dog extends Animal {
speak() {
const parentSound = super.speak(); // Call parent
return `${parentSound} — but I'm a dog, so: Woof!`;
}
}
console.log(new Dog().speak()); // "Animal sound — but I'm a dog, so: Woof!"
When to use each:
| Use Replace When | Use Extend When |
|---|---|
| Parent’s behavior is completely wrong for the subclass | You want to add to parent’s behavior |
| You need a totally different implementation | You want to reuse parent logic |
| Parent’s method doesn’t apply to the subclass | You want to customize, not replace |
d. Calling Parent Method with super
The super.method() syntax lets you call the parent’s version of an overridden method.
class Vehicle {
start() {
return "Vehicle starting...";
}
}
class Car extends Vehicle {
start() {
const base = super.start();
return `${base} Car engine running.`;
}
}
const car = new Car();
console.log(car.start());
// "Vehicle starting... Car engine running."
Rules for super.method():
- Only available in methods of subclasses (classes with
extends) - Always refers to the parent class’s method
- Can be called from any method (not just constructors)
- Works with any method, not just
constructor
e. Real-World Examples
Example 1: Shape Hierarchy with Polymorphism
class Shape {
constructor(name) {
this.name = name;
}
area() { return 0; }
perimeter() { return 0; }
describe() {
return `${this.name}: area=${this.area().toFixed(2)}, perimeter=${this.perimeter().toFixed(2)}`;
}
}
class Circle extends Shape {
constructor(radius) {
super('Circle');
this.radius = radius;
}
area() { return Math.PI * this.radius ** 2; }
perimeter() { return 2 * Math.PI * this.radius; }
}
class Rectangle extends Shape {
constructor(w, h) {
super('Rectangle');
this.w = w;
this.h = h;
}
area() { return this.w * this.h; }
perimeter() { return 2 * (this.w + this.h); }
}
class Square extends Rectangle {
constructor(side) {
super(side, side);
this.name = 'Square'; // Override name property
}
// Overrides Rectangle.describe? No, inherits it
// But area() and perimeter() from Rectangle work perfectly
}
const shapes = [
new Circle(5),
new Rectangle(4, 6),
new Square(3)
];
shapes.forEach(s => console.log(s.describe()));
// Circle: area=78.54, perimeter=31.42
// Rectangle: area=24.00, perimeter=20.00
// Square: area=9.00, perimeter=12.00
Note: Square doesn’t override area() or perimeter() — it inherits from Rectangle because a square is a rectangle with equal sides.
Example 2: Employee Hierarchy
class Employee {
constructor(name, salary) {
this.name = name;
this.salary = salary;
}
getRole() {
return 'Employee';
}
getBonus() {
return this.salary * 0.05; // 5% default
}
toString() {
return `${this.name} — ${this.getRole()} — Bonus: $${this.getBonus().toFixed(2)}`;
}
}
class Manager extends Employee {
constructor(name, salary, department) {
super(name, salary);
this.department = department;
}
getRole() {
return `Manager (${this.department})`;
}
// Extend parent's getBonus — managers get more
getBonus() {
return super.getBonus() * 2; // 2× the default bonus
}
}
class Developer extends Employee {
constructor(name, salary, language) {
super(name, salary);
this.language = language;
}
getRole() {
return `Developer (${this.language})`;
}
getBonus() {
return super.getBonus() * 1.5; // 1.5× the default
}
}
const employees = [
new Employee('Alice', 50000),
new Manager('Bob', 90000, 'Engineering'),
new Developer('Carol', 75000, 'JavaScript')
];
employees.forEach(e => console.log(e.toString()));
// Alice — Employee — Bonus: $2500.00
// Bob — Manager (Engineering) — Bonus: $9000.00 (2× of $4500)
// Carol — Developer (JavaScript) — Bonus: $5625.00 (1.5× of $3750)
Key insight: Manager.getBonus() calls super.getBonus() — reusing the parent’s calculation, then modifying it.
Example 3: Custom Error Hierarchy
class AppError extends Error {
constructor(message, code) {
super(message);
this.name = 'AppError';
this.code = code;
this.timestamp = new Date();
}
// Default toString
toString() {
return `[${this.name}] ${this.message} (code: ${this.code})`;
}
}
class ValidationError extends AppError {
constructor(field, value) {
super(`Invalid value for ${field}: "${value}"`, 'VALIDATION');
this.name = 'ValidationError';
this.field = field;
this.value = value;
}
// Extend parent's toString
toString() {
return super.toString() + ` — field: ${this.field}`;
}
}
class NotFoundError extends AppError {
constructor(resource) {
super(`${resource} not found`, 'NOT_FOUND');
this.name = 'NotFoundError';
this.resource = resource;
}
// Fully override toString (no super)
toString() {
return `404 — ${this.resource} does not exist`;
}
}
const err1 = new ValidationError('email', 'not-an-email');
const err2 = new NotFoundError('User');
console.log(err1.toString());
// "[ValidationError] Invalid value for email: "not-an-email" (code: VALIDATION) — field: email"
console.log(err2.toString());
// "404 — User does not exist"
console.log(err1 instanceof ValidationError); // true
console.log(err1 instanceof AppError); // true
console.log(err1 instanceof Error); // true
Example 4: 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;
const base = super.attack();
return `${base} Warrior 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 a spell for 30 damage! (Mana: ${this.mana})`;
}
return `${this.name} is out of mana!`;
}
}
class Healer extends Character {
constructor(name) {
super(name, 100);
}
attack() {
return `${this.name} does 5 damage (weak attack).`;
}
heal(target) {
target.hp += 20;
return `${this.name} heals ${target.name} for 20 HP.`;
}
}
const warrior = new Warrior('Conan');
const mage = new Mage('Merlin');
const healer = new Healer('Elara');
console.log(warrior.attack());
// "Conan attacks for 10 damage. Warrior rage: 10"
console.log(mage.attack());
// "Merlin casts a spell for 30 damage! (Mana: 80)"
console.log(healer.attack());
// "Elara does 5 damage (weak attack)."
console.log(healer.heal(warrior));
// "Elara heals Conan for 20 HP."
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Class Method Override</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: 0.95em;
background: #f8f9fa;
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border-left: 4px solid #007bff;
white-space: pre-wrap;
line-height: 1.8;
}
.log-display {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 8px;
font-family: 'Courier New', monospace;
font-size: 0.85rem;
max-height: 250px;
overflow-y: auto;
margin: 10px 0;
}
.log-entry { margin: 3px 0; }
.log-success { color: #4ec9b0; }
.log-info { color: #569cd6; }
.log-warn { color: #dcdcaa; }
.input-group {
margin: 10px 0;
}
.input-group label {
display: inline-block;
min-width: 120px;
font-weight: bold;
}
.input-group input {
padding: 8px 12px;
border: 2px solid #ddd;
border-radius: 6px;
font-size: 1em;
width: 150px;
}
.input-group input:focus {
outline: none;
border-color: #007bff;
}
</style>
</head>
<body>
<h1>Class Method Override</h1>
<div class="demo-box">
<h2>1. Basic Method Override</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="comment">// Override</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="comment">// Override</span>
}
}
</pre>
</div>
<div class="demo-box">
<h2>2. Override vs Extend</h2>
<div class="hierarchy">
<span style="color: #4ec9b0;">REPLACE (complete override)</span>
─────────────────────────────────
<span style="color: #569cd6;">class</span> <span style="color: #4ec9b0;">Dog</span> <span style="color: #569cd6;">extends</span> <span style="color: #4ec9b0;">Animal</span> {
speak() {
<span style="color: #569cd6;">return</span> <span style="color: #ce9178;">"Woof!"</span>; <span style="color: #6a9955;">← Replaces parent entirely</span>
}
}
<span style="color: #4ec9b0;">EXTEND (call parent + add)</span>
─────────────────────────────────
<span style="color: #569cd6;">class</span> <span style="color: #4ec9b0;">Dog</span> <span style="color: #569cd6;">extends</span> <span style="color: #4ec9b0;">Animal</span> {
speak() {
<span style="color: #569cd6;">const</span> parent = <span style="color: #569cd6;">super</span>.speak(); <span style="color: #6a9955;">← Calls parent</span>
<span style="color: #569cd6;">return</span> parent + <span style="color: #ce9178;">" Woof!"</span>; <span style="color: #6a9955;">← Adds new behavior</span>
}
}
</div>
</div>
<div class="demo-box">
<h2>3. Method Resolution</h2>
<pre>
<span class="keyword">const</span> dog = <span class="keyword">new</span> <span class="class-name">Dog</span>();
dog.<span class="function">speak</span>();
│
├── Is speak on dog? <span class="comment">No</span>
├── Is speak on Dog.proto? <span class="comment">Yes!</span> ← Uses this
└── Is speak on Animal? <span class="comment">(not needed)</span>
</pre>
</div>
<div class="demo-box">
<h2>4. Comparison Table</h2>
<table>
<tr>
<th>Approach</th>
<th>Behavior</th>
<th>Uses super?</th>
<th>When to Use</th>
</tr>
<tr>
<td><strong>Override (Replace)</strong></td>
<td>Replaces parent's method entirely</td>
<td>❌ No</td>
<td>Parent's logic doesn't apply</td>
</tr>
<tr>
<td><strong>Extend (super)</strong></td>
<td>Calls parent + adds behavior</td>
<td>✅ Yes</td>
<td>Customizing parent's behavior</td>
</tr>
<tr>
<td><strong>Inherit</strong></td>
<td>Uses parent's method as-is</td>
<td>—</td>
<td>Parent behavior is perfect</td>
</tr>
</table>
</div>
<div class="demo-box">
<h2>5. Interactive: Shape Override Demo</h2>
<div style="margin: 10px 0;">
<button class="btn" onclick="showAllShapes()">Show All Shapes</button>
<button class="btn btn-success" onclick="addCircle()">Add Circle</button>
<button class="btn btn-success" onclick="addRectangle()">Add Rectangle</button>
<button class="btn btn-success" onclick="addSquare()">Add Square</button>
<button class="btn" onclick="compareOverrides()">Compare Overrides</button>
</div>
<div class="class-display" id="shapeDisplay">Click a button to explore method overriding</div>
</div>
<div class="demo-box">
<h2>6. Interactive: Employee Bonus (Extend)</h2>
<div style="margin: 10px 0;">
<button class="btn" onclick="showEmployees()">Show All Employees</button>
<button class="btn btn-success" onclick="addEmployee()">Add Employee</button>
<button class="btn btn-success" onclick="addManager()">Add Manager</button>
<button class="btn btn-success" onclick="addDeveloper()">Add Developer</button>
</div>
<div class="class-display" id="employeeDisplay">Click a button to see bonus calculations</div>
</div>
<div class="demo-box">
<h2>7. Live Output — All Examples</h2>
<div id="output">Loading...</div>
</div>
<script>
// ============================================
// Class Method Override — Live Demo
// ============================================
let results = [];
// 1. Basic override
results.push('📌 Basic Method Override:\n');
class Animal {
speak() {
return "Some generic animal sound";
}
}
class Dog extends Animal {
speak() {
return "Woof!";
}
}
class Cat extends Animal {
speak() {
return "Meow!";
}
}
const animal = new Animal();
const dog = new Dog();
const cat = new Cat();
results.push(' new Animal().speak() → "' + animal.speak() + '"');
results.push(' new Dog().speak() → "' + dog.speak() + '"');
results.push(' new Cat().speak() → "' + cat.speak() + '"');
results.push('');
// 2. Override (replace)
results.push('📌 Override (Replace Parent):\n');
class Shape {
area() {
return 0;
}
describe() {
return `Shape with 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 Square extends Rectangle {
constructor(side) {
super(side, side);
this.name = 'Square';
}
area() {
return super.area(); // Calls Rectangle.area()
}
describe() {
return `Square with side ${this.w}, area ${this.area().toFixed(2)}`;
}
}
const circle = new Circle(5);
const rect = new Rectangle(4, 6);
const square = new Square(3);
results.push(' circle.describe() → "' + circle.describe() + '"');
results.push(' rect.describe() → "' + rect.describe() + '"');
results.push(' square.describe() → "' + square.describe() + '"');
results.push('');
// 3. Extend with super
results.push('📌 Extend with super.method():\n');
class Vehicle {
start() {
return "Vehicle starting...";
}
}
class Car extends Vehicle {
start() {
const base = super.start();
return `${base} Car engine running.`;
}
}
const car = new Car();
results.push(' new Vehicle().start() → "' + new Vehicle().start() + '"');
results.push(' new Car().start() → "' + car.start() + '"');
results.push(' → Car calls super.start() then adds behavior');
results.push('');
// 4. Employee bonus — extend parent
results.push('📌 Employee Bonus (Extending Parent):\n');
class Employee {
constructor(name, salary) {
this.name = name;
this.salary = salary;
}
getRole() {
return 'Employee';
}
getBonus() {
return this.salary * 0.05;
}
toString() {
return `${this.name} (${this.getRole()}) — Bonus: $${this.getBonus().toFixed(2)}`;
}
}
class Manager extends Employee {
constructor(name, salary, department) {
super(name, salary);
this.department = department;
}
getRole() {
return `Manager, ${this.department}`;
}
getBonus() {
return super.getBonus() * 2;
}
}
class Developer extends Employee {
constructor(name, salary, language) {
super(name, salary);
this.language = language;
}
getRole() {
return `Developer (${this.language})`;
}
getBonus() {
return super.getBonus() * 1.5;
}
}
const employees = [
new Employee('Alice', 50000),
new Manager('Bob', 90000, 'Engineering'),
new Developer('Carol', 75000, 'JavaScript')
];
employees.forEach(e => results.push(' ' + e.toString()));
results.push('');
// 5. Shape hierarchy with polymorphism
results.push('📌 Polymorphism (Same Method, Different Behavior):\n');
const shapes = [new Circle(5), new Rectangle(4, 6), new Square(3)];
shapes.forEach(s => {
results.push(' ' + s.constructor.name + '.area() → ' + s.area().toFixed(2));
});
results.push('');
// 6. Game characters
results.push('📌 Game Characters (Override):\n');
class Character {
constructor(name, hp) {
this.name = name;
this.hp = hp;
}
attack() {
return `${this.name} attacks for 10 damage.`;
}
describe() {
return `${this.name} (HP: ${this.hp})`;
}
}
class Warrior extends Character {
constructor(name) {
super(name, 150);
this.rage = 0;
}
attack() {
this.rage += 10;
const base = super.attack();
return `${base} 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 a spell for 30 damage! (Mana: ${this.mana})`;
}
return `${this.name} is out of mana!`;
}
}
const warrior = new Warrior('Conan');
const mage = new Mage('Merlin');
results.push(' ' + warrior.attack());
results.push(' ' + warrior.attack());
results.push(' ' + mage.attack());
results.push(' ' + mage.attack());
results.push('');
// 7. Custom errors
results.push('📌 Custom Errors (Override toString):\n');
class AppError extends Error {
constructor(message, code) {
super(message);
this.name = 'AppError';
this.code = code;
}
toString() {
return `[${this.name}] ${this.message} (code: ${this.code})`;
}
}
class ValidationError extends AppError {
constructor(field, value) {
super(`Invalid value for ${field}: "${value}"`, 'VALIDATION');
this.name = 'ValidationError';
this.field = field;
}
toString() {
return super.toString() + ` — field: ${this.field}`;
}
}
class NotFoundError extends AppError {
constructor(resource) {
super(`${resource} not found`, 'NOT_FOUND');
this.name = 'NotFoundError';
this.resource = resource;
}
toString() {
return `404 — ${this.resource} does not exist`;
}
}
const err1 = new ValidationError('email', 'not-an-email');
const err2 = new NotFoundError('User');
results.push(' ' + err1.toString());
results.push(' ' + err2.toString());
results.push('');
results.push(' Instance checks:');
results.push(' err1 instanceof ValidationError → ' + (err1 instanceof ValidationError));
results.push(' err1 instanceof AppError → ' + (err1 instanceof AppError));
results.push(' err1 instanceof Error → ' + (err1 instanceof Error));
results.push(' err2 instanceof NotFoundError → ' + (err2 instanceof NotFoundError));
document.getElementById('output').textContent = results.join('\n');
// ============================================
// Interactive: Shape Override Demo
// ============================================
const interactiveShapes = [];
function showAllShapes() {
const display = document.getElementById('shapeDisplay');
if (interactiveShapes.length === 0) {
display.textContent = 'No shapes yet. Click "Add Circle", "Add Rectangle", or "Add Square".';
return;
}
let text = 'All Shapes:\n';
text += '─────────────────────\n';
interactiveShapes.forEach((s, i) => {
text += `${i + 1}. ${s.describe()}\n`;
});
display.textContent = text;
}
function addCircle() {
const r = Math.floor(Math.random() * 8) + 2;
interactiveShapes.push(new Circle(r));
showAllShapes();
}
function addRectangle() {
const w = Math.floor(Math.random() * 8) + 2;
const h = Math.floor(Math.random() * 8) + 2;
interactiveShapes.push(new Rectangle(w, h));
showAllShapes();
}
function addSquare() {
const side = Math.floor(Math.random() * 8) + 2;
interactiveShapes.push(new Square(side));
showAllShapes();
}
function compareOverrides() {
const display = document.getElementById('shapeDisplay');
display.textContent =
`Method Override Comparison:\n` +
`─────────────────────────────\n` +
`\n` +
`Shape.area() → returns 0 (base)\n` +
`Circle.area() → OVERRIDES with πr²\n` +
`Rectangle.area() → OVERRIDES with w×h\n` +
`Square.area() → EXTENDS Rectangle (uses super)\n` +
`\n` +
`Circle.describe() → INHERITS from Shape\n` +
`Square.describe() → OVERRIDES Shape.describe()\n` +
`\n` +
`→ Same method name, different implementations!`;
}
// ============================================
// Interactive: Employee Bonus (Extend)
// ============================================
const interactiveEmployees = [
new Employee('Alice', 50000),
new Manager('Bob', 90000, 'Engineering'),
new Developer('Carol', 75000, 'JavaScript')
];
function showEmployees() {
const display = document.getElementById('employeeDisplay');
let text = 'All Employees:\n';
text += '─────────────────────\n';
interactiveEmployees.forEach((e, i) => {
text += `${i + 1}. ${e.toString()}\n`;
});
display.textContent = text;
}
function addEmployee() {
const n = Math.floor(Math.random() * 10000) + 40000;
interactiveEmployees.push(new Employee('Employee', n));
showEmployees();
}
function addManager() {
const n = Math.floor(Math.random() * 20000) + 70000;
const depts = ['Engineering', 'Sales', 'Marketing', 'HR'];
const dept = depts[Math.floor(Math.random() * depts.length)];
interactiveEmployees.push(new Manager('Manager', n, dept));
showEmployees();
}
function addDeveloper() {
const n = Math.floor(Math.random() * 15000) + 60000;
const langs = ['JavaScript', 'Python', 'Rust', 'Go'];
const lang = langs[Math.floor(Math.random() * langs.length)];
interactiveEmployees.push(new Developer('Developer', n, lang));
showEmployees();
}
// Initialize
showEmployees();
</script>
</body>
</html>
Quick Reference
Method Resolution Order
dog.speak()
│
├── 1. Look on dog (own properties) → not found
├── 2. Look on Dog.prototype → found! ✅
└── 3. (Not reached) Animal.prototype
Rule: JavaScript looks up the prototype chain from most specific to least specific — the first matching method wins.
Override vs Extend
| Approach | Syntax | Behavior |
|---|---|---|
| Override (Replace) | method() { ... } | Replaces parent method |
| Extend | method() { super.method(); ... } | Calls parent + adds |
When to Override
| Scenario | Approach |
|---|---|
| Parent behavior doesn’t apply | Replace |
| Need to customize parent | Extend with super |
| Parent behavior is fine | Inherit (don’t override) |
| Add new behavior only | Add new method |
super.method() Rules
| Rule | Description |
|---|---|
| Only in subclass | Must have extends |
| Refers to parent | Always the immediate parent |
| Any method | Not just constructors |
Preserves this | this still refers to the instance |
Best Practices
✅ Do This:
// Override when behavior is completely different
class Dog extends Animal {
speak() {
return "Woof!";
}
}
// Use super to extend, not replace
class Manager extends Employee {
getBonus() {
return super.getBonus() * 2; // Reuse parent calculation
}
}
// Keep overridden methods consistent in purpose
class Shape {
area() { return 0; }
}
class Circle extends Shape {
area() { return Math.PI * this.radius ** 2; } // Same purpose
}
// Call super.method() to preserve parent logic
class Logger extends BaseLogger {
log(message) {
super.log(message); // Parent's logic
console.log(`[${new Date().toISOString()}] ${message}`); // Extra
}
}
❌ Don’t Do This:
// Don't override methods with different signatures
class Parent {
greet(name) { }
}
class Child extends Parent {
greet() { } // ❌ Different signature — confusing!
}
// Don't forget to call super when extending
class Child extends Parent {
greet() {
// Missing super.greet() — parent logic lost!
return "Child";
}
}
// Don't override methods to do unrelated things
class Shape {
area() { return 0; }
}
class Circle extends Shape {
area() { return "I am a circle"; } // ❌ Should return a number
}
// Don't call super in a class that doesn't extend
class MyClass {
method() {
super.method(); // ❌ SyntaxError
}
}
Common Pitfalls
| Pitfall | Problem | Solution |
|---|---|---|
Forgetting super | Parent logic lost | Call super.method() |
Calling super outside class | SyntaxError | Only in subclass methods |
| Overriding with different signature | Confusing API | Keep consistent |
| Not overriding when needed | Wrong behavior | Override when specialized |
| Overriding private methods | Not possible | Use regular methods |
Real-World Example: Logger Hierarchy
class Logger {
log(message) {
console.log(`[LOG] ${message}`);
}
error(message) {
console.error(`[ERROR] ${message}`);
}
}
class TimestampLogger extends Logger {
log(message) {
const ts = new Date().toISOString();
super.log(`${ts} — ${message}`);
}
error(message) {
const ts = new Date().toISOString();
super.error(`${ts} — ${message}`);
}
}
class PrefixedLogger extends TimestampLogger {
constructor(prefix) {
super();
this.prefix = prefix;
}
log(message) {
super.log(`[${this.prefix}] ${message}`);
}
}
const logger = new PrefixedLogger('API');
logger.log('Server started');
// "[LOG] 2024-01-15T10:30:00.000Z — [API] Server started"
Chain: Logger → TimestampLogger → PrefixedLogger — each adds a layer.
Pro Tip: Method overriding is the heart of polymorphism in OOP. Use it to:
- Specialize behavior — subclasses do things differently
- Extend behavior — call
super.method()+ add - Replace behavior — completely redefine
Key rules:
- Same method name as parent
- Use
super.method()to call parent’s version - Keep method signatures consistent
- Override for behavior changes, not signature changes
Remember: If the parent’s behavior is perfect, don’t override — just inherit! Override only when you need different or additional behavior. And use super.method() whenever you want to build on the parent’s logic instead of replacing it.
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!