The extends Keyword
extends creates a child class that inherits all instance and static members from a parent class. The child's constructor must call super() before accessing this.
class Animal {
constructor(name, species) {
this.name = name;
this.species = species;
this.energy = 100;
}
eat(food) {
this.energy += 10;
return `${this.name} eats ${food}. Energy: ${this.energy}`;
}
toString() {
return `[${this.species}] ${this.name}`;
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name, 'Canis lupus familiaris'); // required before this
this.breed = breed;
}
bark() {
this.energy -= 5;
return `${this.name} barks! Energy: ${this.energy}`;
}
}
const rex = new Dog('Rex', 'German Shepherd');
console.log(rex.toString()); // inherited from Animal
console.log(rex.eat('bone')); // inherited method
console.log(rex.bark()); // own method
[Canis lupus familiaris] Rex Rex eats bone. Energy: 110 Rex barks! Energy: 105
Method Overriding with super.method()
A child class can override any parent method by declaring a method with the same name. Use super.methodName() inside the override to call the parent's version before or after adding child-specific behaviour.
class Animal {
constructor(name) {
this.name = name;
}
describe() {
return `I am ${this.name}`;
}
sound() {
return '...';
}
}
class Cat extends Animal {
constructor(name, indoor) {
super(name);
this.indoor = indoor;
}
// Override: extends the parent version
describe() {
const base = super.describe(); // call parent
return `${base}, a ${this.indoor ? 'indoor' : 'outdoor'} cat`;
}
// Override: fully replaces parent
sound() {
return 'Meow!';
}
}
class Lion extends Cat {
constructor(name) {
super(name, false); // outdoor
}
sound() {
return 'ROAR!'; // overrides Cat.sound()
}
}
const kitty = new Cat('Kitty', true);
const simba = new Lion('Simba');
console.log(kitty.describe()); // uses super
console.log(kitty.sound());
console.log(simba.describe()); // inherits Cat.describe
console.log(simba.sound()); // Lion.sound
I am Kitty, a indoor cat Meow! I am Simba, a outdoor cat ROAR!
instanceof and the Prototype Chain
instanceof walks the prototype chain of an object to check whether any link equals Constructor.prototype. You can also inspect the chain programmatically with Object.getPrototypeOf().
class A {}
class B extends A {}
class C extends B {}
const c = new C();
console.log(c instanceof C); // true
console.log(c instanceof B); // true
console.log(c instanceof A); // true
console.log(c instanceof Object); // true (everything inherits Object)
// Walking the chain manually
let proto = Object.getPrototypeOf(c);
while (proto !== null) {
console.log(proto.constructor.name);
proto = Object.getPrototypeOf(proto);
}
// C β B β A β Object
true true true true C B A Object
Multi-Level Inheritance Chain
Inheritance chains can go multiple levels deep: Animal β Dog β ServiceDog. Each constructor calls super() to chain upwards, and each level can add its own properties and methods.
class Animal {
constructor(name) { this.name = name; }
eat() { return `${this.name} is eating.`; }
sleep(){ return `${this.name} is sleeping.`; }
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
bark() { return `${this.name} barks: Woof!`; }
}
class ServiceDog extends Dog {
constructor(name, breed, role) {
super(name, breed);
this.role = role;
this.onDuty = false;
}
startDuty() {
this.onDuty = true;
return `${this.name} (${this.role}) is now on duty.`;
}
assist(person) {
if (!this.onDuty) return `${this.name} is off duty.`;
return `${this.name} assists ${person} as a ${this.role}.`;
}
}
const lassie = new ServiceDog('Lassie', 'Collie', 'Search & Rescue');
console.log(lassie.eat()); // from Animal
console.log(lassie.bark()); // from Dog
console.log(lassie.startDuty()); // from ServiceDog
console.log(lassie.assist('John')); // from ServiceDog
Lassie is eating. Lassie barks: Woof! Lassie (Search & Rescue) is now on duty. Lassie assists John as a Search & Rescue.
Mixin Pattern β Multiple Inheritance Workaround
JavaScript classes can only extend one parent. Mixins simulate multiple inheritance by composing small behaviour objects into a class using Object.assign or higher-order functions.
// Mixin factories β return objects with methods
const Serializable = (Base) => class extends Base {
serialize() { return JSON.stringify(this); }
static deserialize(json) { return Object.assign(new this(), JSON.parse(json)); }
};
const Timestamped = (Base) => class extends Base {
constructor(...args) {
super(...args);
this.createdAt = new Date().toISOString();
}
};
class Entity {
constructor(id) { this.id = id; }
}
class User extends Timestamped(Serializable(Entity)) {
constructor(id, name) {
super(id);
this.name = name;
}
}
const u = new User(1, 'Alice');
console.log(u.serialize());
console.log(u.createdAt.slice(0, 10)); // today's date
{"id":1,"name":"Alice","createdAt":"2026-06-04T..."}
2026-06-04
Inheritance chains deeper than 2β3 levels become hard to reason about. Favour mixins or simple object composition when you need to share behaviour across unrelated classes.
Classical vs Prototypal Inheritance
| Aspect | Classical (Java/C++) | Prototypal (JavaScript) |
|---|---|---|
| Based on | Classes (blueprints) | Objects (live prototypes) |
| Copies behaviour? | Yes β class body copied at compile | No β delegates to prototype at runtime |
| Multiple inheritance | Some languages support it | One [[Prototype]] link; use mixins |
| Modify shared behaviour | Recompile class | Mutate Constructor.prototype at runtime |
| ES6 classes | Syntactic sugar | Still prototype-based underneath |
Adding methods to Array.prototype or Object.prototype in production code can silently conflict with future ECMAScript features or third-party libraries. Always extend by creating a subclass instead.
ποΈ Practical Exercise
Model a vehicle hierarchy:
- Vehicle β
make,model,speed=0; methodsaccelerate(n)andbrake(n). - ElectricVehicle extends Vehicle β adds
batteryLevel;charge(kWh)adds to battery;accelerate(n)overrides to also drain battery byn * 0.1. - Tesla extends ElectricVehicle β adds
autopilot()method. - Verify
instanceofreturnstrueat every level of the chain.
π₯ Challenge Exercise
Create two mixins β Loggable (adds log(msg) that prefixes the class name) and Validatable (adds validate(rules) that checks own properties against a rules object and returns an array of error strings). Compose both onto a FormField base class and demonstrate validation with error messages.
Interview Questions
- What does
extendsdo to the prototype chain of a class? - Why must
super()be called beforethisin a child constructor? - How does
instanceofdetermine if an object is an instance of a class? - What is method overriding and how do you call the parent method from the override?
- How do mixins simulate multiple inheritance in JavaScript?
- What is the difference between classical and prototypal inheritance?
π Summary
class Child extends Parentsets up the prototype chain and inherits all methods.super()in a child constructor must be called before anythisaccess.super.method()calls the parent class's version of a method.- Method overriding lets child classes replace or extend parent behaviour.
instanceofwalks the full prototype chain β a ServiceDog is also a Dog and an Animal.- Mixins using higher-order class factories are the idiomatic way to share behaviour across unrelated classes.
Frequently Asked Questions
Yes. You can class MyArray extends Array and methods like map() and filter() will return instances of MyArray (thanks to Symbol.species). However, extending built-ins has some edge cases in older transpilers β test carefully if you target legacy environments.
JavaScript auto-generates constructor(...args) { super(...args); } for you, forwarding all arguments to the parent. You only need to write a constructor when you want to add child-specific initialisation.
Object.create(proto) creates a plain object with its [[Prototype]] set to proto β useful for prototypal inheritance without classes. extends wires up both the instance prototype chain and the static/constructor chain, and enforces super() calls. Use extends with classes and Object.create() when working with plain objects.