Class Inheritance
extends lets one class build on another. The new class inherits both instance and static members, may add its own, and may override the parent's. The keyword super is the bridge that lets the child reach back into the parent — for the parent constructor, or for a parent method that the child has overridden.
The basic shape
class Animal {
constructor(name) {
this.name = name;
}
describe() {
return `I am ${this.name}`;
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // must call before touching `this`
this.breed = breed;
}
describe() {
// Reach the parent method via super
return `${super.describe()}, a ${this.breed}`;
}
}
const d = new Dog("Rex", "labrador");
console.log(d.describe()); // 'I am Rex, a labrador'
console.log(d instanceof Dog); // true
console.log(d instanceof Animal); // truesuper in the constructor
In a derived class constructor, this does not exist until you call super(...). Trying to read or write this before that throws.
class Dog extends Animal {
constructor(name, breed) {
// this.breed = breed; // ReferenceError — must super first
super(name);
this.breed = breed; // ok now
}
}super in methods
Inside a method, super.foo() looks up foo on the parent prototype. this is still the actual instance, so any state changes go where you expect.
class Base {
speak() { return "..."; }
describe() {
return `Base says: ${this.speak()}`;
}
}
class Loud extends Base {
speak() { return "HELLO"; }
describe() {
return super.describe().toUpperCase();
}
}
console.log(new Loud().describe()); // 'BASE SAYS: HELLO'Notice that super.describe() calls the parent method, but inside that parent method this is still the Loud instance — so this.speak() resolves to the overridden version. That is dynamic dispatch, and it works across inheritance.
Overriding instance fields
Subclass instance fields are assigned after super() returns. So a parent constructor cannot see the child's field defaults during its own setup.
class Base {
type = "base";
constructor() {
console.log("Base sees:", this.type);
}
}
class Child extends Base {
type = "child";
}
new Child();Base sees: base
The base constructor runs while type is still "base". Only after super() finishes does the child's field declaration overwrite it.
Inheriting static members
Statics inherit too: a subclass can call its parent's static methods through itself, and override them by declaring a static method of the same name.
class Animal {
static create(name) { return new this(name); } // 'this' is the actual subclass
constructor(name) { this.name = name; }
}
class Cat extends Animal {}
const c = Cat.create("Mittens");
console.log(c instanceof Cat); // true — not just Animalinstanceof and the prototype chain
extends wires up two prototype links — one for instances, one for the class objects themselves.
Child.prototypeinherits fromParent.prototype— that is what makes inherited instance methods work.The function
Childitself inherits fromParent— that is what makes inherited static methods work.a instanceof Parentwalks upa's prototype chain looking forParent.prototype, so subclass instances are also instances of their parents.
class A {}
class B extends A {}
console.log(Object.getPrototypeOf(B.prototype) === A.prototype); // true
console.log(Object.getPrototypeOf(B) === A); // true
console.log(new B() instanceof A); // trueExtending built-ins
You can extend native classes like Error, Array, Map, and Set. The result behaves like the native, with whatever you add on top.
class HttpError extends Error {
constructor(status, message) {
super(message);
this.name = "HttpError";
this.status = status;
}
}
try {
throw new HttpError(404, "not found");
} catch (e) {
console.log(e instanceof Error, e.status, e.message);
}true 404 not found
When inheritance is the wrong tool
Resist deep hierarchies. Two or three levels is usually plenty.
Prefer composition when reuse is about sharing behaviour, not "this is a kind of that".
A method that needs to look at the subclass to decide what to do is a sign the abstraction is leaky.
For mix-and-match capabilities, see Mixins — multiple inheritance is not directly supported.