Utvidelse av basisklassemetoder med flere nivåer av arv (typescript)
Jeg har 3 klasser:
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";
}
}
}
Jeg forventet at klasse C sin DoStuff() skulle kalle B sin DoStuff() (som igjen ville kalle A sin).
Men å kalle DoStuff() på C returnerer bare «Called from A and Called from C». Hva gjør jeg galt her? Bør ikke dette også kalle metoden til B?
Et fungerende eksempel på dette kan finnes her:
Eksempel
Svar #1
Når du trenger å bruke super, bruk klassemetoder i stedet for klassemedlemmer:
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());
Prøv det (skriver Ut Kalt fra A og Kalt fra B og Kalt fra C)
Dette er fordi super oversettes til .prototype, det vil si at super.DoStuff() blir til _super.prototype.DoStuff() og de eneste tingene som er tilgjengelige på .prototype er klassemetoder.
Mer: http://basarat.github.io/TypeScriptDeepDive/#/super
Svar #2
JavaScript har egentlig ikke metoder. TypeScript prøver å skjule hvordan prototypekjeden fungerer med kjent notasjon, men som alle abstraksjoner er den ufullkommen. Hovedpoenget med prototypekjeden er at når du kaller c.DoStuff(), ser den etter DoStuff på c-instansen, deretter på prototypen (C), så prototypen til den (B), deretter prototypen til den (A), og til slutt prototypen til (Object).
Det gjelder ikke bare for funksjoner, men for ethvert medlem du måtte slå opp. Funksjoner er ikke spesielt unike i JavaScript, noe som ironisk nok er det som gjør dem kraftfulle.
Ved å bruke TypeScripts syntaks i klassen din A legger du en funksjon DoStuff i A sin prototype. Hver instans av A du kaller DoStuff på, vil lete etter den i instansen (og sannsynligvis feile med å finne den der), se på prototypen og se den funksjonen du definerte. Så langt, så bra.
Deretter definerte du en klasse B som utvider A, så prototypekjeden er B->A->Object. Inne i konstruktøren til B tildeler du en ny kopi av en funksjon til DoStuff på hver eneste instans. (Dette bruker mye mer minne.) Så når du oppretter en ny B og kaller DoStuff på den, finnes det en funksjon der uten å noen gang sjekke prototypen. Så det fungerer.
Nå definerer du en klasse C som utvider B, og prototypekjeden er C->B->A->Object. Igjen får hver instans av C en kopi av en funksjon tildelt DoStuff. Men inne i den konstruktøren henter vi ikke DoStuff fra en instans av B, vi henter den fra prototypen, og vi la ikke til den funksjonen direkte til prototypen. Når vi ikke finner den der, går vi opp prototypekjeden til A.prototype og finner et DoStuff-medlem å bruke derfra. Det er derfor C har en referanse til A sin DoStuff.
Du kunne få koden din til å fungere som forventet hvis du gjorde noe slikt:
class B extends A {
constructor() {
super();
}
}
B.prototype.DoStuff = function () {
return A.prototype.DoStuff() + " and Called from B";
}
Men det er bare tull.
