2013-05-30 2 views
7

У меня есть 3 класса:Продление методы базовых классов с несколькими уровнями наследования (машинопись)

class A{ 
    DoStuff(){ 
     return "Called from A"; 
    } 
} 

class B extends A { 

    constructor(){ 
     super(); 
     var baseDoStuff = super.DoStuff; 
     this.DoStuff = function(){ 
      return baseDoStuff() + " and Called from B"; 
     } 
    } 
} 

class C extends B { 
    constructor(){ 
     super(); 
     var baseDoStufffff = super.DoStuff; 
     this.DoStuff = function(){ 
      return baseDoStufffff() + " and Called from C"; 
     } 
    } 
} 

я ожидал DoStuff класс С (в), чтобы вызвать DoStuff Б() (который, в свою очередь, могли бы назвать 'ы).

Однако вызов DoStuff() на C возвращает только «Вызывается из A и вызывается с C». Что я здесь делаю неправильно? Разве это не следует использовать метод B?

Рабочий пример этого можно найти здесь:

Example

ответ

9

Всякий раз, когда вам нужно использовать super использовать класс методы вместо класса членов:

class A{ 
    DoStuff(){ 
     return "Called from A"; 
    } 
} 

class B extends A { 

    constructor(){ 
     super();  

    } 
    DoStuff(){ 
     return super.DoStuff() + " and Called from B"; 
    } 
} 

class C extends B { 
    constructor(){ 
     super();     
    } 

    DoStuff(){ 
      return super.DoStuff() + " and Called from C"; 
    } 
} 

var c = new C(); 
console.log(c.DoStuff()); 

Try it (отпечатки, вызываемые из А и вызывается из В и вызывается из C)

Это потому, что super переводит к .prototype т.е. super.DoStuff() становится _super.prototype.DoStuff() и только вещи, доступные на .prototype методы класса.

Подробнее: http://basarat.github.io/TypeScriptDeepDive/#/super

5

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"; 
} 

Но это просто глупо.

Смежные вопросы