Почему переопределение в подклассе не срабатывает, когда метод — стрелочное поле класса?
Базовый класс определяет обработчик как стрелочное поле класса; подкласс пытается переопределить его как обычный метод прототипа. После подписки обоих инстансов для инстанса подкласса выполняется только поведение базового класса — investingAccount остаётся 0.
Объясните, почему переопределение в подклассе игнорируется, затем исправьте так, чтобы поведение подкласса выполнялось, а обработчик при этом сохранял свой this при передаче как отвязанный колбэк.
class Person {
savingsAccount = 0;
getPayment = (amount) => { this.savingsAccount += amount; };
}
class Investor extends Person {
investingAccount = 0;
getPayment(amount) { // задуманное переопределение
this.investingAccount += Math.floor(amount / 2);
this.savingsAccount += Math.ceil(amount / 2);
}
}
const investor = new Investor();
const handler = investor.getPayment; // передан как отвязанный колбэк
handler(1000);
console.log(investor.investingAccount); // 0 — почему?
Определите причину и исправьте.
Стрелочное поле класса — это собственное свойство инстанса, присваиваемое в конструкторе, а не метод прототипа. Поэтому investor.getPayment находит сначала поле базового класса, а метод подкласса на Investor.prototype перекрыт — переопределение не срабатывает.
- ✗Думать, что метод прототипа переопределяет собственное стрелочное поле класса с тем же именем
- ✗Считать, что поля класса и методы прототипа лежат в одном месте при поиске
- ✗Смешивать стрелочное поле в базовом классе с методом прототипа в подклассе
- →Где на цепочке поиска лежит собственное поле класса против метода прототипа?
- →Что даёт стрелочное поле базового класса, чего лишается обычный метод прототипа при отвязывании?
Решение
Стрелочное поле класса (getPayment = (amount) => …) — это собственное свойство инстанса, создаваемое инициализатором поля в конструкторе. Метод подкласса getPayment(amount){…} лежит на Investor.prototype. При поиске investor.getPayment движок сначала находит собственное свойство (стрелочное поле от Person) и не доходит до прототипа — поле перекрывает метод. Поэтому выполняется поведение базового класса, а investingAccount остаётся 0.
// Исправление: оба — методы прототипа; привязываем this в конструкторе.
class Person {
savingsAccount = 0;
constructor() {
this.getPayment = this.getPayment.bind(this); // сохраняем this как у колбэка
}
getPayment(amount) { this.savingsAccount += amount; }
}
class Investor extends Person {
investingAccount = 0;
getPayment(amount) { // теперь реально переопределяет
this.investingAccount += Math.floor(amount / 2);
this.savingsAccount += Math.ceil(amount / 2);
}
}
const investor = new Investor();
const handler = investor.getPayment;
handler(1000);
console.log(investor.investingAccount); // 500
Привязка в конструкторе Investor свяжет уже переопределённый метод, потому что bind в Person-конструкторе берёт this.getPayment, который к моменту его выполнения уже разрешается через прототип подкласса.