klass arv är ett sätt för en klass att utöka en annan klass.

så att vi kan skapa nya funktioner ovanpå befintliga.

sökordet ”utökar”

låt oss säga att vi har klassAnimal:

Så här kan vi representeraanimal objekt ochAnimal klass grafiskt:

Animal klass grafiskt:

…och vi vill skapa en annan class Rabbit.,

eftersom kaniner är djur,Rabbit klass bör baseras påAnimal, har tillgång till djurmetoder, så att kaniner kan göra vad ”generiska” djur kan göra.

syntaxen för att utöka en annan klass är:class Child extends Parent.

låt oss skapaclass Rabbit som ärver frånAnimal:

objekt förRabbit klassen har åtkomst både tillRabbit metoder, till exempelrabbit.hide(), och även tillRabbit metoder, tillmetoder, till exempel rabbit.run().,

Internt fungerarextends – sökordet med den gamla goda prototypmekaniken. Den anger Rabbit.prototype.] till Animal.prototype. Så, om en metod inte finns i Rabbit.prototype, tar JavaScript det från Animal.prototype.,

till exempel för att hitta rabbit.run metod, motorn kontrollerar (bottom-up på bilden):

som vi kan återkalla från kapitlet native prototyper använder JavaScript sig prototypiskt arv för inbyggda objekt. Till exempelDate.prototype.] ärObject.prototype. Därför har Datum tillgång till generiska objektmetoder.,

alla uttryck är tillåtna efterextends

Klasssyntax gör det möjligt att ange inte bara en klass, utan alla uttryck efterextends.

till exempel ett funktionssamtal som genererar moderklassen:

function f(phrase) { return class { sayHi() { alert(phrase); } };}class User extends f("Hello") {}new User().sayHi(); // Hello

Här class User ärver från resultatet av f("Hello")

class User ärver från resultatet av f("Hello").,

det kan vara användbart för avancerade programmeringsmönster när vi använder funktioner för att generera klasser beroende på många villkor och kan ärva från dem.

åsidosätter en metod

låt oss nu gå vidare och åsidosätta en metod. Som standard tas alla metoder som inte anges i class Rabbit direkt ”som är” från class Animal.,

men om vi anger vår egen metod i Rabbit, till exempel stop() då kommer den att användas istället:

class Rabbit extends Animal { stop() { // ...now this will be used for rabbit.stop() // instead of stop() from class Animal }}

vanligtvis vill vi inte helt ersätta en överordnad metod, utan snarare att bygga på toppen av den för att skapa en justera eller utöka dess funktionalitet. Vi gör något i vår metod, men kallar föräldermetoden före / efter det eller i processen.

klasser ger"super" nyckelord för det.

  • super.method(...) för att anropa en överordnad metod.,
  • super(...) för att ringa en överordnad konstruktör (endast inom vår konstruktör).

låt till exempel vår kanin autohide när den stoppas:

nuRabbit har metodenstop som anropar föräldernsuper.stop() I processen.

övergripande konstruktör

med konstruktörer blir det lite knepigt.

hittills hade Rabbit inte sin egen constructor.,

Enligt specifikationen, om en klass utökar en annan klass och inte har någon constructor, genereras följande ”tomma” constructor:

class Rabbit extends Animal { // generated for extending classes without own constructors constructor(...args) { super(...args); }}

som vi kan se, kallas det i princip den överordnadeconstructor

skickar alla argument. Det händer om vi inte skriver en egen konstruktör.

låt oss nu lägga till en egen konstruktör tillRabbit. Det kommer att ange earLength förutom name:

Whoops!, Vi har ett fel. Nu kan vi inte skapa kaniner. Vad gick fel?

det korta svaret är:

  • konstruktörer i ärva klasser måste ringa super(...), och (!) gör det innan du använder this.

…men varför? Vad händer här? Faktum är att kravet verkar konstigt.

naturligtvis finns det en förklaring. Låt oss gå in på detaljer, så du förstår verkligen vad som händer.

i JavaScript skiljer man mellan en konstruktörsfunktion i en ärvklass (så kallad ”härledd konstruktör”) och andra funktioner., En härledd konstruktör har en speciell intern egenskap ]:"derived". Det är en speciell intern etikett.

den etiketten påverkar dess beteende mednew.

  • när en vanlig funktion körs med new skapar den ett tomt objekt och tilldelar det till this.
  • men när en härledd konstruktör körs gör den inte det här. Det förväntar sig att den överordnade konstruktören ska göra det här jobbet.,

så en härledd konstruktör måste ringa superför att kunna utföra sin överordnad (bas) konstruktör, annars kommer inte objektet för this att skapas. Och vi får ett fel.

förRabbit – konstruktören måste den ringasuper() innan du använderthis, som här:

övergripande klassfält: en knepig anteckning

avancerad anteckning

den här noten förutsätter att du har en viss erfarenhet av klasser, kanske på andra programmeringsspråk.,

det ger bättre inblick i språket och förklarar också beteendet som kan vara en källa till buggar (men inte så ofta).

om du har svårt att förstå, fortsätt bara, Fortsätt läsa och återvänd sedan till det en tid senare.

Vi kan åsidosätta inte bara metoder utan även klassfält.

även om det finns ett knepigt beteende när vi kommer åt ett åsidosatt fält i överordnad konstruktör, helt annorlunda än de flesta andra programmeringsspråk.,

Tänk på detta exempel:

här, klassRabbit utökarAnimal och åsidosättername fält med eget värde.

det finns ingen egen konstruktör i Rabbit, så Animal konstruktören heter.

det som är intressant är att i båda fallen: new Animal() och new Rabbit() visar alert I raden (*) animal.,

med andra ord använder den överordnade konstruktören alltid sitt eget fältvärde, inte det åsidosatta.

vad är konstigt med det?

om det inte är klart ännu, jämför med metoder.

här är samma kod, men i stället för this.name fält vi kallar this.showName() metod:

Observera: nu är utgången annorlunda.

och det är vad vi naturligtvis förväntar oss. När den överordnade konstruktören anropas i den härledda klassen använder den åsidosatta metoden.

…men för klassfält är det inte så. Som sagt använder den överordnade konstruktören alltid det överordnade fältet.,

Varför är det skillnaden?

Tja, orsaken är i fältet initieringsordning. Klassfältet initieras:

  • innan konstruktören för basklassen (som inte sträcker sig något),
  • omedelbart efter super() för den härledda klassen.

i vårt fall ärRabbit den härledda klassen. Det finns ingen constructor() I den. Som sagt tidigare är det samma som om det fanns en tom konstruktör med endast super(...args).,

så,new Rabbit() anroparsuper(), alltså exekvera den överordnade konstruktören, och (per regeln för härledda klasser) först efter att dess klassfält initieras. Vid tidpunkten för utförandet av den överordnade konstruktören finns det inga Rabbit klassfält än, det är därför Animal fält används.

denna subtila skillnad mellan fält och metoder är specifik för JavaScript

lyckligtvis avslöjar detta beteende endast sig om ett åsidosatt fält används i den överordnade konstruktören., Då kan det vara svårt att förstå vad som händer, så vi förklarar det här.

om det blir ett problem kan man åtgärda det genom att använda metoder eller getters/setters istället för fält.

Super: internals,]

avancerad information

om du läser handledningen för första gången-det här avsnittet kan hoppas över.

det handlar om de interna mekanismerna bakom arv och super.

låt oss få lite djupare under huven påsuper. Vi får se intressanta saker på vägen.,

först att säga, från allt som vi har lärt oss hittills, är det omöjligt för super att arbeta alls!

ja, låt oss verkligen fråga oss själva, hur det ska fungera Tekniskt? När en objektmetod körs blir det aktuella objektet som this. Om vi kallar super.method() måste motorn hämta method från prototypen för det aktuella objektet. Men hur?

uppgiften kan verka enkel, men det är den inte., Motorn känner till det aktuella objektet this, så det kan få den överordnade method som this.__proto__.method. Tyvärr kommer en sådan” naiv ” lösning inte att fungera.

låt oss visa problemet. Utan klasser, med hjälp av vanliga föremål för enkelhetens skull.

Du kan hoppa över den här delen och gå under till underavsnittet] om du inte vill veta detaljerna. Det skadar inte. Eller läs vidare om du är intresserad av att förstå saker på djupet.

i exemplet nedan rabbit.__proto__ = animal., Låt oss nu försöka: i rabbit.eat() ringer vi animal.eat(), med this.__proto__:

på linjen (*) tar vi eat från prototypen (animal) och ring det i samband med det aktuella objektet. Observera att.call(this) är viktigt här, eftersom en enkelthis.__proto__.eat() skulle köra föräldereat I samband med prototypen, inte det aktuella objektet.,

och i koden ovan fungerar den faktiskt som avsedd: vi har rätt alert.

låt oss nu lägga till ytterligare ett objekt i kedjan. Vi får se hur saker går sönder:

koden fungerar inte längre! Vi kan se felet som försöker ringa longEar.eat().

det kanske inte är så uppenbart, men om vi spårar longEar.eat() samtal kan vi se varför. I båda raderna (*) och (**) värdet på this är det aktuella objektet (longEar)., Det är viktigt: alla objektmetoder får det aktuella objektet som this, inte en prototyp eller något.

här är bilden av vad som händer:

problemet kan inte lösas genom att använda ensam.

]

För att tillhandahålla lösningen lägger JavaScript till ytterligare en speciell intern egenskap för funktioner:].,

När en funktion anges som en klass-eller objektmetod blir dess] – egenskap det objektet.

super använder den för att lösa den överordnade prototypen och dess metoder.

låt oss se hur det fungerar, först med vanliga objekt:

metoder är inte ”fria”

som vi har känt tidigare är funktionerna i allmänhet ”fria”, inte bundna till objekt i JavaScript. Så de kan kopieras mellan objekt och kallas med en annan this.,

själva existensen av ] bryter mot den principen, eftersom metoder kommer ihåg sina objekt. ] kan inte ändras, så detta band är för alltid.

den enda platsen på språket där ]används – är super. Så, om en metod inte använder super, kan vi fortfarande betrakta det gratis och kopiera mellan objekt. Men medsuper saker kan gå fel.,

här är demo av ett fel super resultat efter kopiering:

ett samtal till tree.sayHi() visar”jag är ett djur”. Definitivt fel.

anledningen är enkel:

här är diagrammet över vad som händer:

metoder, inte Funktionsegenskaper

skillnaden kan vara icke-väsentlig för oss, men det är viktigt för JavaScript.

i exemplet nedan används en icke-metodsyntax för jämförelse., ] egenskapen är inte inställd och arv fungerar inte:

sammanfattning

  1. för att utöka en klass:class Child extends Parent:
    • Det betyderChild.prototype.__proto__ kommer att varaParent.prototype, så metoder ärvs.
  2. när du åsidosätter en konstruktör:
    • Vi måste ringa överordnad konstruktör som super() I Child konstruktör innan du använder this.,
  3. när du åsidosätter en annan metod:
    • vi kan användasuper.method()I enChildmetod för att ringaParent metod.
  4. Internals:
    • metoder minns deras klass/objekt i den interna] egenskapen. Så lösersuper överordnade metoder.
    • så det är inte säkert att kopiera en metod medsuper från ett objekt till ett annat.,

också:

  • Pilfunktioner har inte sina egnathis ellersuper, så att de passar in i det omgivande sammanhanget.