Glossary

Inheritance

Inheritance is an OOP mechanism where a child class automatically receives the properties and methods of its parent class, allowing code reuse and creating an 'is-a' relationship between types.

Explanation

In JavaScript, inheritance is implemented with extends. The child class (subclass) inherits all non-private methods and properties from the parent class (superclass). The child can: add new methods, override parent methods with new implementations, and call the parent's version via super.methodName(). This creates an "is-a" relationship: a Dog is-a Animal, a PremiumUser is-a User. JavaScript uses prototype-based inheritance under the hood. extends sets up the prototype chain: instances of Dog have Dog.prototype as their prototype, which has Animal.prototype as its prototype. When you call rex.speak(), JavaScript looks for speak on rex (not found), then on Dog.prototype (not found), then on Animal.prototype (found — executes it). This chain explains why instanceof checks work transitively: rex instanceof Animal is true. The "composition over inheritance" principle advises preferring object composition (building complex objects by combining simpler ones) over deep inheritance hierarchies. Problems with deep inheritance: tight coupling (changes to the base class break all subclasses), the fragile base class problem (a "safe" change to a base method unexpectedly breaks subclass behavior), and the Gorilla-banana problem (you wanted a banana but got a gorilla holding a banana and the entire jungle). Modern JavaScript design (React hooks, functional composition) favors composition. Single inheritance (one parent class) is JavaScript's model. Multiple inheritance (inheriting from two or more classes) is not supported in JavaScript, but mixins (adding methods from multiple sources to a class's prototype) are a common pattern.

Code Example

javascript
// Inheritance: Animal → Dog → GuideDog

class Animal {
  constructor(name) { this.name = name; }
  speak() { return `${this.name} makes a sound`; }
  toString() { return `Animal(${this.name})`; }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);          // MUST call super() first
    this.breed = breed;
  }

  speak() {               // override parent method
    return `${this.name} barks`;
  }

  fetch() { return `${this.name} fetches the ball`; }
}

class GuideDog extends Dog {
  constructor(name, breed, owner) {
    super(name, breed);
    this.owner = owner;
  }

  speak() {
    // Call parent's speak and extend it
    return super.speak() + ' (guide dog, very gentle)';
  }
}

const buddy = new GuideDog('Buddy', 'Lab', 'Alice');
console.log(buddy.speak());   // "Buddy barks (guide dog, very gentle)"
console.log(buddy.fetch());   // inherited from Dog
console.log(buddy instanceof GuideDog); // true
console.log(buddy instanceof Animal);   // true

// Composition alternative: mixins
const Swimmable = Base => class extends Base {
  swim() { return `${this.name} swims`; }
};
class SwimmingDog extends Swimmable(Dog) {}

Why It Matters for Engineers

Inheritance errors are a common class of bugs in OOP code: forgetting super() in a child constructor, overriding a method without calling super.method() when the parent logic is needed, or creating deep hierarchies that become unmaintainable. Knowing how inheritance works — not just the syntax — helps you avoid these pitfalls and evaluate when to reach for composition instead. The composition vs inheritance debate also has practical implications: React's component model is built on composition (parent components pass data and children to child components) rather than class inheritance. Understanding why React's architects chose composition helps you write idiomatic React.

Related Terms

Class · Polymorphism · Encapsulation · Abstraction

Learn This In Practice

Go deeper with the full module on Beyond Vibe Code.

Programming Foundations → →