Inheritance
Sharing behavior between classes. Parent to child relationships.
extends
Creates a child class that inherits from a parent.
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return `${this.name} makes a noise.`;
}
}
class Dog extends Animal {
bark() {
return `${this.name} barks!`;
}
}
const rex = new Dog("Rex");
rex.speak(); // "Rex makes a noise." (inherited)
rex.bark(); // "Rex barks!" (own method)
rex instanceof Dog; // true
rex instanceof Animal; // trueWhat extends does:
1. Sets Dog.prototype.__proto__ = Animal.prototype
2. Sets Dog.__proto__ = Animal (static inheritance)
3. Dog inherits all methods from Animal
super()
Calls the parent constructor or parent methods.
class Animal {
constructor(name, legs) {
this.name = name;
this.legs = legs;
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name, 4); // calls Animal's constructor
this.breed = breed; // then set own props
}
}
const rex = new Dog("Rex", "Labrador");
rex.name; // "Rex" (from parent)
rex.legs; // 4 (from parent)
rex.breed; // "Labrador" (own)super() in constructor calls the parent constructor. Must be called BEFORE using 'this'.
Method Overriding
Replace or extend inherited behavior.
class Shape {
area() {
return 0;
}
describe() {
return `Shape with area ${this.area()}`;
}
}
class Circle extends Shape {
constructor(radius) {
super();
this.radius = radius;
}
// Override: completely replace parent method
area() {
return Math.PI * this.radius ** 2;
}
}
class Square extends Shape {
constructor(side) {
super();
this.side = side;
}
area() {
return this.side ** 2;
}
}
const c = new Circle(5);
c.area(); // 78.54 (overridden)
c.describe(); // "Shape with area 78.54" (inherited, calls OUR area())💡 When the parent calls this.area(), it uses the child's version if overridden. This is polymorphism.
Multilevel Inheritance
Chains of inheritance: grandparent → parent → child.
class LivingThing {
isAlive() { return true; }
}
class Animal extends LivingThing {
constructor(name) {
super();
this.name = name;
}
eat() { return `${this.name} eats`; }
}
class Dog extends Animal {
bark() { return "Woof!"; }
}
const d = new Dog("Rex");
d.bark(); // "Woof!" (own)
d.eat(); // "Rex eats" (from Animal)
d.isAlive(); // true (from LivingThing)
d instanceof Dog; // true
d instanceof Animal; // true
d instanceof LivingThing; // true⚠️ Deep hierarchies (4+ levels) become hard to maintain. Prefer composition for complex object structures.
Composition vs Inheritance
'Has-a' (composition) is often better than 'is-a' (inheritance).
// ✗ Inheritance approach (fragile):
// class FlyingSwimmingDog extends ???
// Can't inherit from multiple classes!
// ✓ Composition approach (flexible):
const canFly = (obj) => ({
fly() { return `${obj.name} is flying`; }
});
const canSwim = (obj) => ({
swim() { return `${obj.name} is swimming`; }
});
function createDuck(name) {
const duck = { name };
return Object.assign(duck, canFly(duck), canSwim(duck));
}
const donald = createDuck("Donald");
donald.fly(); // "Donald is flying"
donald.swim(); // "Donald is swimming"Inheritance
- • Single parent only
- • Tight coupling
- • Fragile hierarchies
- • Good for "is-a" relationships
Composition
- • Mix multiple behaviors
- • Loose coupling
- • Easy to change
- • Good for "has-a" relationships
FAQ
Common questions about inheritance.