Ad – 728Γ—90
πŸ—οΈ OOP

JavaScript Inheritance – extends and super

Inheritance lets one class reuse and extend the behaviour of another. With ES6's extends keyword, child classes gain every method of the parent and can override or augment them with super. This lesson covers single-level and multi-level inheritance, method overriding, the instanceof operator, mixins for multiple-inheritance workarounds, and the underlying prototypal mechanics that make it all work.

⏱️ 22 min read 🎯 Intermediate πŸ“… Updated 2026

The extends Keyword

extends creates a child class that inherits all instance and static members from a parent class. The child's constructor must call super() before accessing this.

JavaScript
class Animal {
  constructor(name, species) {
    this.name    = name;
    this.species = species;
    this.energy  = 100;
  }

  eat(food) {
    this.energy += 10;
    return `${this.name} eats ${food}. Energy: ${this.energy}`;
  }

  toString() {
    return `[${this.species}] ${this.name}`;
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name, 'Canis lupus familiaris'); // required before this
    this.breed = breed;
  }

  bark() {
    this.energy -= 5;
    return `${this.name} barks! Energy: ${this.energy}`;
  }
}

const rex = new Dog('Rex', 'German Shepherd');
console.log(rex.toString());   // inherited from Animal
console.log(rex.eat('bone'));  // inherited method
console.log(rex.bark());       // own method
β–Ά Output
[Canis lupus familiaris] Rex
Rex eats bone. Energy: 110
Rex barks! Energy: 105

Method Overriding with super.method()

A child class can override any parent method by declaring a method with the same name. Use super.methodName() inside the override to call the parent's version before or after adding child-specific behaviour.

JavaScript
class Animal {
  constructor(name) {
    this.name = name;
  }
  describe() {
    return `I am ${this.name}`;
  }
  sound() {
    return '...';
  }
}

class Cat extends Animal {
  constructor(name, indoor) {
    super(name);
    this.indoor = indoor;
  }

  // Override: extends the parent version
  describe() {
    const base = super.describe();   // call parent
    return `${base}, a ${this.indoor ? 'indoor' : 'outdoor'} cat`;
  }

  // Override: fully replaces parent
  sound() {
    return 'Meow!';
  }
}

class Lion extends Cat {
  constructor(name) {
    super(name, false);  // outdoor
  }

  sound() {
    return 'ROAR!';   // overrides Cat.sound()
  }
}

const kitty = new Cat('Kitty', true);
const simba = new Lion('Simba');

console.log(kitty.describe());   // uses super
console.log(kitty.sound());
console.log(simba.describe());   // inherits Cat.describe
console.log(simba.sound());      // Lion.sound
β–Ά Output
I am Kitty, a indoor cat
Meow!
I am Simba, a outdoor cat
ROAR!

instanceof and the Prototype Chain

instanceof walks the prototype chain of an object to check whether any link equals Constructor.prototype. You can also inspect the chain programmatically with Object.getPrototypeOf().

JavaScript
class A {}
class B extends A {}
class C extends B {}

const c = new C();

console.log(c instanceof C);        // true
console.log(c instanceof B);        // true
console.log(c instanceof A);        // true
console.log(c instanceof Object);   // true (everything inherits Object)

// Walking the chain manually
let proto = Object.getPrototypeOf(c);
while (proto !== null) {
  console.log(proto.constructor.name);
  proto = Object.getPrototypeOf(proto);
}
// C β†’ B β†’ A β†’ Object
β–Ά Output
true
true
true
true
C
B
A
Object

Multi-Level Inheritance Chain

Inheritance chains can go multiple levels deep: Animal β†’ Dog β†’ ServiceDog. Each constructor calls super() to chain upwards, and each level can add its own properties and methods.

JavaScript
class Animal {
  constructor(name) { this.name = name; }
  eat()  { return `${this.name} is eating.`; }
  sleep(){ return `${this.name} is sleeping.`; }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }
  bark() { return `${this.name} barks: Woof!`; }
}

class ServiceDog extends Dog {
  constructor(name, breed, role) {
    super(name, breed);
    this.role = role;
    this.onDuty = false;
  }

  startDuty() {
    this.onDuty = true;
    return `${this.name} (${this.role}) is now on duty.`;
  }

  assist(person) {
    if (!this.onDuty) return `${this.name} is off duty.`;
    return `${this.name} assists ${person} as a ${this.role}.`;
  }
}

const lassie = new ServiceDog('Lassie', 'Collie', 'Search & Rescue');
console.log(lassie.eat());           // from Animal
console.log(lassie.bark());          // from Dog
console.log(lassie.startDuty());     // from ServiceDog
console.log(lassie.assist('John'));  // from ServiceDog
β–Ά Output
Lassie is eating.
Lassie barks: Woof!
Lassie (Search & Rescue) is now on duty.
Lassie assists John as a Search & Rescue.

Mixin Pattern – Multiple Inheritance Workaround

JavaScript classes can only extend one parent. Mixins simulate multiple inheritance by composing small behaviour objects into a class using Object.assign or higher-order functions.

JavaScript
// Mixin factories – return objects with methods
const Serializable = (Base) => class extends Base {
  serialize()   { return JSON.stringify(this); }
  static deserialize(json) { return Object.assign(new this(), JSON.parse(json)); }
};

const Timestamped = (Base) => class extends Base {
  constructor(...args) {
    super(...args);
    this.createdAt = new Date().toISOString();
  }
};

class Entity {
  constructor(id) { this.id = id; }
}

class User extends Timestamped(Serializable(Entity)) {
  constructor(id, name) {
    super(id);
    this.name = name;
  }
}

const u = new User(1, 'Alice');
console.log(u.serialize());
console.log(u.createdAt.slice(0, 10));  // today's date
β–Ά Output
{"id":1,"name":"Alice","createdAt":"2026-06-04T..."}
2026-06-04
πŸ’‘
Prefer composition over deep inheritance

Inheritance chains deeper than 2–3 levels become hard to reason about. Favour mixins or simple object composition when you need to share behaviour across unrelated classes.

Classical vs Prototypal Inheritance

AspectClassical (Java/C++)Prototypal (JavaScript)
Based onClasses (blueprints)Objects (live prototypes)
Copies behaviour?Yes – class body copied at compileNo – delegates to prototype at runtime
Multiple inheritanceSome languages support itOne [[Prototype]] link; use mixins
Modify shared behaviourRecompile classMutate Constructor.prototype at runtime
ES6 classesSyntactic sugarStill prototype-based underneath
⚠️
Do not mutate built-in prototypes

Adding methods to Array.prototype or Object.prototype in production code can silently conflict with future ECMAScript features or third-party libraries. Always extend by creating a subclass instead.

Ad – 336Γ—280

πŸ‹οΈ Practical Exercise

Model a vehicle hierarchy:

  • Vehicle β€” make, model, speed=0; methods accelerate(n) and brake(n).
  • ElectricVehicle extends Vehicle β€” adds batteryLevel; charge(kWh) adds to battery; accelerate(n) overrides to also drain battery by n * 0.1.
  • Tesla extends ElectricVehicle β€” adds autopilot() method.
  • Verify instanceof returns true at every level of the chain.

πŸ”₯ Challenge Exercise

Create two mixins β€” Loggable (adds log(msg) that prefixes the class name) and Validatable (adds validate(rules) that checks own properties against a rules object and returns an array of error strings). Compose both onto a FormField base class and demonstrate validation with error messages.

Interview Questions

  • What does extends do to the prototype chain of a class?
  • Why must super() be called before this in a child constructor?
  • How does instanceof determine if an object is an instance of a class?
  • What is method overriding and how do you call the parent method from the override?
  • How do mixins simulate multiple inheritance in JavaScript?
  • What is the difference between classical and prototypal inheritance?

πŸ“‹ Summary

  • class Child extends Parent sets up the prototype chain and inherits all methods.
  • super() in a child constructor must be called before any this access.
  • super.method() calls the parent class's version of a method.
  • Method overriding lets child classes replace or extend parent behaviour.
  • instanceof walks the full prototype chain β€” a ServiceDog is also a Dog and an Animal.
  • Mixins using higher-order class factories are the idiomatic way to share behaviour across unrelated classes.

Frequently Asked Questions

Can I extend a built-in class like Array? +

Yes. You can class MyArray extends Array and methods like map() and filter() will return instances of MyArray (thanks to Symbol.species). However, extending built-ins has some edge cases in older transpilers β€” test carefully if you target legacy environments.

What if a child class has no constructor? +

JavaScript auto-generates constructor(...args) { super(...args); } for you, forwarding all arguments to the parent. You only need to write a constructor when you want to add child-specific initialisation.

What is the difference between Object.create() and extends? +

Object.create(proto) creates a plain object with its [[Prototype]] set to proto β€” useful for prototypal inheritance without classes. extends wires up both the instance prototype chain and the static/constructor chain, and enforces super() calls. Use extends with classes and Object.create() when working with plain objects.