JavaScript на самом деле не имеют методы. TypeScript пытается скрыть работу prototype chain со знакомой нотацией, но, как и все абстракции, она несовершенна. Суть цепи прототипа заключается в том, что когда вы вызываете c.DoStuff()
, он ищет DoStuff в экземпляре c, чем прототип (C), затем его прототип (B), затем его прототип (A), а затем его прототип (Object).
Это относится не только к функциям, но и к любому члену, которого вы можете найти. Функции не очень специфичны в JavaScript, что по иронии судьбы делает их мощными.
Используя синтаксис TypeScript в вашем классе A, вы помещаете функцию DoStuff в прототип A. Каждый экземпляр A, который вы вызываете DoStuff on, будет искать его в экземпляре (и, вероятно, не находит его там), посмотрите на прототип и посмотрите, какую функцию вы определили. Все идет нормально.
Затем вы определили класс B, который расширяет A, поэтому цепочка прототипов B-> A-> Object. Внутри конструктора B вы назначаете новую копию функции DoStuff на каждый экземпляр . (Это использует намного больше памяти.) Поэтому, когда вы строите новый B и вызываете DoStuff на нем, там есть функция, не анализирующая прототип. Так что работает.
Теперь вы определяете класс C, расширяющий B, а цепь прототипа - C-> B-> A-> Object. Снова каждый экземпляр C получает копию функции, назначенной DoStuff. Но внутри этого конструктора мы не захватываем DoStuff из экземпляра B, мы хватаем его от прототипа, и мы не добавляли эту функцию непосредственно в прототип. Не найдя его там, мы поднимемся вверх по цепочке прототипов до A.prototype и найдем члена DoStuff для использования оттуда. Вот почему C имеет ссылку на DoStuff.
Вы можете сделать свой код работать как ожидается, если вы сделали что-то вроде этого:
class B extends A {
constructor() {
super();
}
}
B.prototype.DoStuff = function() {
return A.prototype.DoStuff() + " and Called from B";
}
Но это просто глупо.