Two classes sharing eighty percent of their code create a familiar problem: every fix has to be made twice, and one of the two always ends up forgotten.
extends is the language answer to that situation. It establishes a lineage between two classes, so that shared code is written only once.
Definition
extends places a class underneath another one. The child class automatically receives the parent methods, can redefine the ones that must change, and can add new ones.
class User {
constructor(name) { this.name = name; }
greet() { return "Hello " + this.name; }
}
class Admin extends User {
greet() { return super.greet() + " (admin)"; }
remove(target) { return this.name + " removes " + target; }
}
const admin = new Admin("Camille");
console.log(admin.greet()); // Hello Camille (admin)
console.log(admin instanceof User); // trueThe Admin class has no constructor: the parent one applies as it is. Its greet method replaces the parent version while still reusing it through super. And an admin remains a user in the eyes of instanceof, which means it can be passed anywhere a user is expected.
What the child class receives
Inheritance covers more than people imagine.
| What is inherited | Detail |
|---|---|
| The parent methods | Available directly on the instance |
| The get and set accessors | On the same footing as methods |
| The static members | Reachable on the child class itself |
| The constructor | Only when the child declares none |
Private fields of the parent, on the other hand, stay out of reach: a child class cannot read a hash-prefixed field declared above it. Exposing internal data to descendants requires an accessor.
Inheriting from built-in classes
The keyword works with the classes of the language, and the most useful case by far is Error.
class HttpError extends Error {
constructor(status) {
super("Request refused with code " + status);
this.name = "HttpError";
this.status = status;
}
}
const error = new HttpError(404);
console.log(error instanceof Error); // true
console.log(error.status); // 404The catching block can then sort errors by type instead of comparing messages. The same mechanism applies to Array or Map, although the need arises far less often there.
A child class that declares its own constructor must call super() before using this. Without that call the language raises an error at construction time: the object does not exist yet.
Frequently asked questions
Can a class inherit from several parents?
No, JavaScript allows a single parent per class. When several independent behaviors have to be combined, the usual answer is to write functions that enrich a class and apply them one after another.
How many levels can be stacked?
The language sets no limit, experience does. Beyond two levels, working out where a method comes from means opening three files, and a change in the root class ripples down to descendants you can no longer see. Two levels cover almost every real need.
Inheritance or composition?
Ask what the link really is: inheritance says an object is a variety of another, composition says an object holds another and hands work to it. When the answer hesitates, composition ages better, because it can be swapped without touching the rest. Keep extends for families of types that are genuinely stable.