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; // true

What 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'.

💡 super() must come before this
1 / 2

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.