Перейти к содержимому

Inheritance — extends & super

В одной фразе: extends устанавливает двойную прототипную цепочку — для экземпляров и для самих классов (static наследование); super() в дочернем конструкторе обязателен до первого обращения к this. На собесе проверяют что именно создаёт extends под капотом.

class Shape {
constructor(color) { this.color = color; }
area() { return 0; }
toString() { return `${this.constructor.name}(${this.color})`; }
}
class Circle extends Shape {
#radius;
constructor(color, radius) {
super(color); // ❗ обязателен до this — иначе ReferenceError
this.#radius = radius; // только после super()
}
area() { // override родительского метода
return Math.PI * this.#radius ** 2;
}
describe() {
return `${super.toString()} r=${this.#radius}`; // super.method()
}
}
// Что создаёт extends под капотом (концептуально):
// 1. Circle.prototype = Object.create(Shape.prototype)
// → экземпляры Circle находят методы Shape через цепочку
// 2. Object.setPrototypeOf(Circle, Shape)
// → статические методы Shape доступны как Circle.staticMethod()
// 3. Circle.prototype.constructor = Circle
const c = new Circle('red', 5);
c instanceof Circle; // true
c instanceof Shape; // true
c instanceof Object; // true
Object.getPrototypeOf(Circle.prototype) === Shape.prototype; // true
Object.getPrototypeOf(Circle) === Shape; // true (static!)
// super.method() работает через [[HomeObject]] — не через this
class Animal {
speak() { return 'animal sound'; }
}
class Dog extends Animal {
speak() {
const parentSound = super.speak(); // [[HomeObject]] = Dog.prototype
return `${parentSound} + bark`;
}
}
new Dog().speak(); // 'animal sound + bark'
// Если дочерний класс не определяет constructor —
// super() вызывается автоматически с forwarding всех аргументов
class Bulldog extends Dog {} // нет constructor — работает корректно
new Bulldog().speak(); // 'animal sound + bark'

Итог: extends создаёт две прототипные цепочки: для экземпляров и для классов. super() создаёт this в дочернем конструкторе — без него this недоступен.