What you'll be able to do

By the end of this page you should be able to spot a deep class tree that will hurt later, model the same feature with composition (small pieces combined), and pick inheritance only when "is-a" truly fits.

Previous post: classes, extends, and super.

Who this is for

  • People who wrote Admin extends User extends Person extends Entity and felt stuck
  • Beginners copying OOP tutorials that subclass everything
  • Anyone building UI or game objects that need mixed behaviors (fly + swim + log)

You can skip this if you already favor small modules over five-level extends. This is judgment, not syntax memorization.

Inheritance — one chain, shared prototype

class Bird {
  fly() { return "flap"; }
}
class Penguin extends Bird {
  // penguins don't fly — awkward override coming
}

Scenario A — true "is-a". Admin extends User when every admin is a user with extra fields — reasonable.

Scenario B — mixed abilities. A logging, payable, exportable invoice is not cleanly one linear chain; forcing it creates empty overrides and fragile super calls.

The rule: inheritance excels when behavior truly layers in one dimension; it struggles when objects need arbitrary bundles of features.

Composition — build by combining parts

function canLog(obj) {
  return {
    log(msg) {
      console.log("[" + obj.name + "] " + msg);
    },
  };
}

function createUser(name) {
  const user = { name };
  return Object.assign(user, canLog(user));
}

Scenario A — feature flags. Add canExport, canPay helpers and merge what you need per object.

Scenario B — React later. Hooks and plain functions often compose; giant class hierarchies are less common in modern UI code.

Modern style also uses class fields with injected dependencies (this.logger = logger) — still composition: the object has a logger, it does not extend Logging.

When each fits (practical, not dogma)

SituationOften works
Shared UI base with one overrideshallow extends
Object needs 3 unrelated behaviorscomposition
Framework requires extends Componentfollow the framework, keep your logic small inside

Scenario A — DOM widget. A Modal has a focus trap and has a close button handler — compose helpers.

Scenario B — typed domain model. Square extends Rectangle can be fine if the math genuinely specializes — but stop before unrelated cross-links.

A tiny practice file

<!DOCTYPE html>
<html>
  <body>
    <button id="go">Compose user</button>
    <pre id="out"></pre>
    <script>
      function withGreet(obj) {
        return {
          greet() {
            return "Hello, " + obj.name;
          },
        };
      }
      function withBadge(obj, badge) {
        return {
          badge() {
            return obj.name + " · " + badge;
          },
        };
      }
      function createMember(name) {
        const base = { name };
        return Object.assign(base, withGreet(base), withBadge(base, "member"));
      }
      document.getElementById("go").addEventListener("click", function () {
        const m = createMember("Asha");
        document.getElementById("out").textContent =
          m.greet() + "\n" + m.badge();
      });
    </script>
  </body>
</html>

No extends — two behaviors merged onto one object.

Mistakes I see a lot

1. Subclassing only to reuse three lines. Extract a function instead.

2. Deep trees where sibling classes duplicate code. Composition or shared utilities fix this faster.

3. "Composition vs inheritance" as a religion. Shallow inheritance plus composition inside is normal.

4. Mocking inheritance in interviews but never using composition in projects. Try both in tiny files so your gut recognizes the smell of a fourth extends.

What to try before the next post

  1. Sketch a bad four-level extends tree; rewrite with two composed helpers.
  2. List "has-a" vs "is-a" for one domain object you know (cart, player, invoice).
  3. Build the tiny HTML file.

Next in this series: closures with real examples — functions that remember outer variables.

Try this next outside the series

These mental models become more useful when you can point at a real framework problem they explain.