Ключове слово super
super дає доступ до батьківського класу зсередини нащадка. Як виклик, super(...), він запускає конструктор батька і створює this; як об'єкт, super.x, він читає члени батьківського прототипу або самої батьківської функції.
Теорія
TL;DR
super(name)у конструкторі викликає конструктор батька і ініціалізуєthis.- До виклику
super()звертання доthisкидаєReferenceError. - У звичайних методах
superвказує наParent.prototype, томуsuper.method()дорівнюєParent.prototype.method.call(this). - У статичних методах
superвказує на самParent, тобто на його статику. super()дозволений тільки всередині класу зextends, інакше цеSyntaxError.- Стрілкові функції не мають власного
superі беруть його із зовнішнього контексту класу.
Швидкий приклад
class Animal {
constructor(name) {
this.name = name;
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // calls Animal.constructor(name)
this.breed = breed;
}
}
const dog = new Dog("Rex", "shepherd");
console.log(dog.name); // "Rex"
console.log(dog.breed); // "shepherd"super() у конструкторі: виклик батька
Що відбувається під капотом, коли один клас успадковує інший:
- До виклику
super()об'єктthisще не існує. - Виклик
super(name)виконує конструктор батька (Animal) і створюєthis, який уже можна використовувати. - Лише після цього можна присвоювати поля в
this.
Якщо спробувати використати this до super(), рушій кине помилку:
class Dog extends Animal {
constructor(name) {
this.name = name; // ReferenceError
super(name);
}
}
ReferenceError: Must call super constructor before accessing 'this'
Причина проста: саме super() ініціалізує контекст екземпляра. Без нього this ще не створено.
super поза конструктором: методи батька
super працює не лише в конструкторі, а і в будь-якому методі класу, щоб викликати батьківську реалізацію.
class Animal {
speak() {
console.log("The animal makes a sound");
}
}
class Dog extends Animal {
speak() {
super.speak(); // calls the parent method
console.log("The dog barks");
}
}
new Dog().speak();
// "The animal makes a sound"
// "The dog barks"Так само зручно нарощувати результат батьківського методу замість того, щоб дублювати його код:
class Animal {
constructor(name) {
this.name = name;
}
info() {
return `Name: ${this.name}`;
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // calls Animal.constructor()
this.breed = breed;
}
info() {
return `${super.info()}, breed: ${this.breed}`;
}
}
const dog = new Dog("Rex", "Beagle");
console.log(dog.info()); // Name: Rex, breed: Beaglesuper у статичних методах
Якщо в класу є статичні методи, super усередині них звертається до статики батька:
class Parent {
static sayHi() {
console.log("Hello from the parent");
}
}
class Child extends Parent {
static sayHi() {
super.sayHi(); // calls Parent.sayHi()
console.log("Hello from the child");
}
}
Child.sayHi();
// Hello from the parent
// Hello from the childНа що насправді вказує super
Під капотом super це посилання на батьківський об'єкт, і цей об'єкт різний залежно від того, де ви пишете super:
- у звичайних методах
superвказує наParent.prototype; - у статичних методах
superвказує на самParent.
Тому виклик:
super.method()еквівалентний:
Parent.prototype.method.call(this)Важлива деталь: this залишається поточним екземпляром, тобто батьківський метод бачить дані нащадка.
super і стрілкові функції
Стрілкові функції не мають власного super і беруть його із зовнішнього контексту, тому в полі-стрілці класу super працює:
class A {
hello() {
console.log("Hello from A");
}
}
class B extends A {
hello = () => {
super.hello(); // works: super is taken from the class body
};
}
new B().hello(); // "Hello from A"Пам'ятайте лише, що методи-стрілки створюються на екземплярі, а не в прототипі, тож кожен об'єкт отримує власну копію функції.
Зведення:
| Де використовується | Що робить |
|---|---|
| У конструкторі | Викликає конструктор батька і створює this |
| У методі | Викликає батьківський метод (super.method()) |
| У статичному методі | Звертається до статичних методів батька |
Без extends | Помилка |
До виклику super() | this недоступний, буде помилка |
Типові помилки
- Виклик
super()у класі безextends: цеSyntaxError, бо батька просто немає.
class A {
constructor() {
super(); // SyntaxError
}
}- Присвоєння полів у
thisдоsuper(). Порядок завжди такий: спершуsuper(...), потімthis.x = .... - Пропущений
super()у конструкторі нащадка: конструктор завершиться помилкою, щойно торкнетьсяthis. - Очікування, що
super.method()викличе метод зthis.__proto__. Ні,superприв'язаний до класу, у якому написаний метод, а не до типу поточного об'єкта. - Спроба написати
super.constructor()замістьsuper(). Це різні речі: створює екземпляр самеsuper().
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.