The super Keyword
The super keyword is how a subclass talks to the class it extends. It lets a child constructor invoke its parent’s constructor and lets any method reach up to the parent’s version of a method it overrides. Understanding super — and the strict ordering rules around it — is essential for writing correct class hierarchies in JavaScript, because misusing it produces some of the language’s most confusing runtime errors.
Calling the parent constructor with super()
When a class extends another class, its constructor must call super() before it can use this. The super() call runs the parent constructor, which is what actually initializes the this object for the derived class. Until that call completes, this is in a “temporal dead zone” and any reference to it throws a ReferenceError.
class Animal {
constructor(name) {
this.name = name;
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // initializes `this` via Animal's constructor
this.breed = breed;
}
}
const d = new Dog("Rex", "Labrador");
console.log(`${d.name} is a ${d.breed}`);
Output:
Rex is a Labrador
If you flip the order and touch this first, the engine rejects it:
class Cat extends Animal {
constructor(name) {
this.indoor = true; // ReferenceError: must call super first
super(name);
}
}
Output:
ReferenceError: Must call super constructor in derived class before accessing 'this'
If a derived class omits its constructor entirely, JavaScript supplies a default one that forwards all arguments:
constructor(...args) { super(...args); }. So you only need to write a constructor when you add or transform arguments.
Calling parent methods with super.method()
Outside the constructor, super.methodName() calls the parent class’s implementation of a method, even when the child has overridden it. This is the standard way to extend behavior rather than fully replace it — do the parent’s work, then add your own.
class Logger {
log(message) {
return `[LOG] ${message}`;
}
}
class TimestampLogger extends Logger {
log(message) {
const base = super.log(message); // reuse parent behavior
return `${new Date().toISOString()} ${base}`;
}
}
const t = new TimestampLogger();
console.log(t.log("server started"));
Output:
2026-06-01T09:00:00.000Z [LOG] server started
super also works inside getters, setters, and static methods. In a static method, super refers to the parent class itself rather than its prototype:
class Shape {
static describe() {
return "a generic shape";
}
}
class Circle extends Shape {
static describe() {
return `${super.describe()}, specifically a circle`;
}
}
console.log(Circle.describe());
Output:
a generic shape, specifically a circle
Ordering rules and common gotchas
The placement of super() in a derived constructor is governed by precise rules. The table below summarizes when super is required and what it binds to in each context.
| Context | What super does | Required? |
|---|---|---|
| Derived constructor | super(args) runs parent constructor, binds this | Yes, before using this |
| Base (non-extending) constructor | Not allowed — there is no parent | No (SyntaxError if used) |
| Instance method | super.m() calls parent prototype method | Optional |
| Static method | super.m() calls parent’s static method | Optional |
| Getter / setter | super.prop reads/writes parent accessor | Optional |
A subtle ordering issue involves class fields. Instance field initializers run immediately after super() returns, so a field you declare on the child is not yet assigned when the parent constructor runs:
class Base {
constructor() {
console.log("Base sees label =", this.label);
}
}
class Derived extends Base {
label = "derived";
constructor() {
super(); // Base runs first
console.log("Derived sees label =", this.label);
}
}
new Derived();
Output:
Base sees label = undefined
Derived sees label = derived
Avoid calling overridable methods from a base constructor that depend on a subclass field — the field won’t exist yet. This is a classic source of
undefinedbugs in inheritance hierarchies.
You can only use super inside methods defined with method syntax or in classes; a regular function assigned as a property has no super binding. Arrow functions, by contrast, inherit super lexically from their enclosing method, which makes them safe to use in callbacks.
Best Practices
- Always call
super()as the very first statement in a derived constructor, before any use ofthis. - Pass through only the arguments the parent actually needs; transform or default the rest after
super()returns. - Prefer
super.method()to extend parent behavior instead of duplicating the parent’s logic in the child. - Omit the constructor entirely when the child adds no new initialization — the default forwarding constructor is correct and clearer.
- Never read subclass fields from inside a base constructor; they are assigned only after
super()completes. - Use arrow functions for callbacks inside methods so they keep the correct
super(andthis) binding.