Skip to content
  • Inheritance: extends & super · JavaScript

Inheritance: extends & super

LearnViaNotes @learnvianotes· 3 min read

extends sets up the prototype chain between two classes; super calls the parent's constructor and methods.

Extending a class

JavaScript
class Shape {
  constructor(name) { this.name = name; }
  area() { return 0; }
  toString() { return `${this.name} with area ${this.area().toFixed(2)}`; }
  static create(...args) { return new this(...args); }   // `this` = the called class
}
class Circle extends Shape {
  constructor(r) {
    super("Circle");           // must come before using `this`
    this.r = r;
  }
  area() { return Math.PI * this.r ** 2; }                // override
}
class Square extends Shape {
  constructor(side) { super("Square"); this.side = side; }
  area() { return this.side ** 2; }
  toString() { return "■ " + super.toString(); }          // extend parent method
}

const shapes = [new Circle(1), Square.create(2)];
for (const s of shapes) console.log(String(s));
console.log(shapes[1] instanceof Square, shapes[1] instanceof Shape);
Output
Circle with area 3.14
■ Square with area 4.00
true true

Rules for super

JavaScript
class Parent {}
class Child extends Parent {
  constructor() {
    this.x = 1;       // before super() → error
    super();
  }
}
try {
  new Child();
} catch (e) {
  console.log(e.name + ": " + e.message);
}
Output
ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
  • A subclass without its own constructor gets constructor(...args) { super(...args); } automatically.

  • super(...) only in constructors; super.method() in any method (also static methods).

  • Parent fields are initialised when super() returns; child fields right after.

Polymorphism and the chain

JavaScript
class Animal { speak() { return "..."; } }
class Dog extends Animal { speak() { return "Woof"; } }
class Puppy extends Dog { speak() { return super.speak() + " (squeaky)"; } }

for (const a of [new Animal(), new Dog(), new Puppy()]) {
  console.log(a.constructor.name.padEnd(6), a.speak());
}
console.log(Object.getPrototypeOf(Puppy) === Dog);                    // static inheritance
console.log(Object.getPrototypeOf(Puppy.prototype) === Dog.prototype); // instance inheritance
Output
Animal ...
Dog    Woof
Puppy  Woof (squeaky)
true
true

Extending built-ins

JavaScript
class Stack extends Array {
  peek() { return this.at(-1); }
}
const s = Stack.from([1, 2, 3]);
s.push(4);
console.log(s.peek(), s.length, s instanceof Array);
console.log(s.map((x) => x * 2) instanceof Stack);   // derived methods return Stack

class ValidationError extends Error {
  constructor(field) {
    super(`${field} is invalid`);
    this.name = "ValidationError";
    this.field = field;
  }
}
const err = new ValidationError("email");
console.log(err.name, err.message, err instanceof Error);
Output
4 4 true
true
ValidationError email is invalid true

Composition over inheritance

Deep hierarchies are brittle. Often it is simpler to compose behaviours: build objects from small functions or mixins.

JavaScript
const canFly = (Base) => class extends Base { fly() { return `${this.name} flies`; } };
const canSwim = (Base) => class extends Base { swim() { return `${this.name} swims`; } };
class Creature { constructor(name) { this.name = name; } }
class Duck extends canSwim(canFly(Creature)) {}

const d = new Duck("Donald");
console.log(d.fly(), "/", d.swim());

// object composition with plain functions
const withLogger = (obj) => ({ ...obj, log: (msg) => `[${obj.name}] ${msg}` });
const service = withLogger({ name: "api" });
console.log(service.log("started"));
Output
Donald flies / Donald swims
[api] started

Why must super() be called before this in a derived constructor?

Does JavaScript support multiple inheritance?

Free for students and teachers

Write notes like this one

Cards for every idea, quizzes and flashcards, read aloud — on the web and your phone.

Something wrong with this note? Open it to report