Lesson 3 of 6 · 11 min · 2 hands-on steps

Inheritance vs Composition

Share behavior the flexible way. One of the most-asked OOP design questions.

Inheritance: "is a"

A Dog is an Animal. With extends, the child class gets the parent's methods and can override them:

JavaScript · try it
class Animal {
  constructor(name) {
    this.name = name;
  }
  speak() {
    return `${this.name} makes a sound`;
  }
}

class Dog extends Animal {
  speak() {
    return `${this.name} barks`;
  }
}

const pets = [new Animal("Generic"), new Dog("Rex")];
for (const p of pets) console.log(p.speak());

Calling the same method on different types and getting the right behavior is called polymorphism.

The problem with deep inheritance

Imagine Animal → Bird → FlyingBird → Duck. Now you need a robot duck that flies and quacks but isn't alive. Where does it go? Deep inheritance trees get rigid fast. Changing a parent breaks every child.

Composition: "has a"

Instead, build objects from small pieces of behavior:

JavaScript · try it
const canFly = (self) => ({ fly: () => `${self.name} flies` });
const canQuack = (self) => ({ quack: () => `${self.name} quacks` });

function makeDuck(name) {
  const self = { name };
  return Object.assign(self, canFly(self), canQuack(self));
}

function makeRobotDuck(name) {
  const self = { name, battery: 100 };
  return Object.assign(self, canFly(self), canQuack(self));
}

console.log(makeRobotDuck("RoboDuck").quack());

The rule of thumb

  • Use inheritance for a true, stable "is a" relationship with a shallow tree (1–2 levels)
  • Use composition for mixing behaviors, which is most of the time

"Favor composition over inheritance" is a classic line from the Design Patterns book, and interviewers love asking why.

🧪 Practice: 2 steps

0 / 2 done

Step 1 of 2 · Inheritance basics

Shape has an area() that returns 0. Create Rectangle and Circle classes that extend Shape and override area().

Then write totalArea(shapes) that returns the sum of all areas (round to 2 decimals).

JavaScript
class Shape {
  area() {
    return 0;
  }
}

class Rectangle extends Shape {
  constructor(w, h) {
    super();
    this.w = w;
    this.h = h;
  }
}

class Circle extends Shape {
  constructor(r) {
    super();
    this.r = r;
  }
}

function totalArea(shapes) {
}

Output appears here. Press Run or Ctrl/⌘ + Enter.

✅ Check your understanding

  1. "Favor composition over inheritance" means…

  2. Inheritance models which relationship?

Create a free account to track your progress.