klasse arv er en måde for en klasse at udvide en anden klasse.

så vi kan skabe ny funktionalitet oven på den eksisterende.

Den “udvider” søgeord

Lad os sige, at vi har klasse Animal:

Her er, hvordan vi kan repræsentere animal objekt og Animal class grafisk:

…Og vi vil gerne skabe en anden class Rabbit.,

Som kaniner er dyr, Rabbit class bør være baseret på Animal, har adgang til dyre metoder, så at kaniner kan gøre, hvad den “generiske” dyr kan gøre.

syntaksen for at udvide en anden klasse er:class Child extends Parent.

Lad os skabe class Rabbit, som arver fra Animal:

Objekt af Rabbit class har adgang til både Rabbit metoder, som f.eks. rabbit.hide() og også at Animal metoder, som f.eks. rabbit.run().,

internt,extends søgeord fungerer ved hjælp af de gode gamle prototype mekanik. Det indstiller Rabbit.prototype.] til Animal.prototype. Så hvis en metode ikke findes i Rabbit.prototype, tager JavaScript det fra Animal.prototype.,

For eksempel, at finde rabbit.run metode, motor kontrollen (bottom-up på billedet):

Som vi kan huske fra kapitel Native prototyper, JavaScript, selv bruger prototypal arv for de indbyggede objekter. F. eks. Date.prototype.] is Object.prototype. Derfor har datoer adgang til generiske objektmetoder.,

et udtryk er tilladt efter extends

Class syntaks, der gør det muligt at specificere ikke kun en klasse, men alle udtryk efter extends.

For eksempel, et funktionskald, der genererer den overordnede klasse:

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

Her class User arver fra resultatet af f("Hello").,

det kan være nyttigt til avancerede programmeringsmønstre, når vi bruger funktioner til at generere klasser afhængigt af mange forhold og kan arve fra dem.

tilsidesætter en metode

lad os nu gå videre og tilsidesætte en metode. Som standard tages alle metoder, der ikke er specificeret i class Rabbit direkte “som de er” fra class Animal.,

Men hvis vi angiver vores egen metode i Rabbit, som f.eks. stop(), så vil det blive brugt i stedet:

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

vi Normalt ikke ønsker at helt at erstatte en overordnet metode, men snarere at bygge oven på det for at tilpasse eller udvide dens funktionalitet. Vi gør noget i vores metode, men kalder den overordnede metode før / efter det eller i processen.

klasser giver"super" nøgleord for det.

  • super.method(...) for at kalde en overordnet metode.,
  • super(...) for at ringe til en overordnet konstruktør (kun inde i vores konstruktør).

For eksempel, lad vores kanin autohide, når stoppet:

Nu Rabbit har stop metode, der kalder forælder super.stop() i processen.

altoverskyggende konstruktør

med konstruktører bliver det lidt vanskeligt.

indtil nu har Rabbit ikke haft sin egen constructor.,

i Henhold til specifikation, hvis en klasse udvider en anden klasse, og har ingen constructor, så følgende “tom” constructor er genereret:

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

Som vi kan se, det dybest set kræver den overordnede constructor passerer det alle argumenter. Det sker, hvis vi ikke skriver en egen konstruktør.

lad os nu tilføje en brugerdefineret konstruktør til Rabbit. Det vil angive earLength ud over name:

ups!, Vi har en fejl. Nu kan vi ikke skabe kaniner. Hvad gik galt?

Det korte svar er:

  • Konstruktører i arve klasser skal ringe super(...), og (!) gør det før du bruger this.

…men hvorfor? Hvad foregår der her? Faktisk synes kravet mærkeligt.

selvfølgelig er der en forklaring. Lad os komme ind i detaljer, så du virkelig forstår, hvad der foregår.

i JavaScript skelnes der mellem en konstruktørfunktion af en arveklasse (såkaldt “afledt konstruktør”) og andre funktioner., En afledt konstruktør har en særlig intern egenskab ]:"derived". Det er en særlig intern etiket.

denne etiket påvirker dens adfærd mednew.

  • når en almindelig funktion udføres med new, opretter den et tomt objekt og tildeler det til this.
  • men når en afledt konstruktør kører, gør den ikke dette. Det forventer, at forældrekonstruktøren skal udføre dette job.,

Så en afledt konstruktøren skal ringe super for at udføre dets moderselskab (base) konstruktør, ellers objekt for this vil ikke blive oprettet. Og vi får en fejl.

For Rabbit constructor til at arbejde, er det nødvendigt at ringe super() før brug af this, som her:

Altoverskyggende klasse felter: en vanskelig bemærk

Avanceret bemærk

Denne bestemmelse forudsætter, at du har en vis erfaring med klasser, måske i andre programmeringssprog.,

det giver bedre indsigt i sproget og forklarer også den adfærd, der kan være en kilde til fejl (men ikke meget ofte).

Hvis du har svært ved at forstå, skal du bare fortsætte, fortsætte med at læse og derefter vende tilbage til det et stykke tid senere.

Vi kan tilsidesætte ikke kun metoder, men også klassefelter.

selvom der er en vanskelig opførsel, når vi får adgang til et overordnet felt i parent constructor, helt anderledes end de fleste andre programmeringssprog.,

Overvej dette eksempel:

Her er klasse Rabbit udvider Animal og tilsidesætter name felt med sin egen værdi.

Der er ingen egen konstruktør i Rabbit, så Animal constructor kaldes.

det interessante er, at i begge tilfælde: new Animal() og new Rabbit() alert i linje (*) viser animal.,

med andre ord bruger forældrekonstruktør altid sin egen feltværdi, ikke den tilsidesættede.

Hvad er mærkeligt om det?

Hvis det ikke er klart endnu, bedes du sammenligne med metoder.

Her er den samme kode, men i stedet for this.name felt, vi kalder this.showName() metode:

bemærk: nu produktion er forskellige.

og det er det, vi naturligvis forventer. Når forældrekonstruktøren kaldes i den afledte klasse, bruger den den tilsidesættede metode.

…men for klassefelter er det ikke sådan. Som sagt bruger forældrekonstruktøren altid forældrefeltet.,

hvorfor er der forskellen?

Nå, årsagen er i feltet initialisering rækkefølge. Klassefeltet initialiseres:

  • før konstruktør for basisklassen (der ikke udvider noget),
  • umiddelbart efter super() for den afledte klasse.

i vores tilfælde er Rabbit den afledte klasse. Der er ingen constructor() i den. Som sagt tidligere er det det samme som om der var en tom konstruktør med kun super(...args).,

så, new Rabbit() kalder super(), således udfører den overordnede konstruktør, og (PR reglen for afledte klasser) først efter at dens klassefelter initialiseres. På tidspunktet for den overordnede konstruktørudførelse er der ingenRabbit klassefelter endnu, det er derforAnimal felter bruges.

denne subtile forskel mellem felter og metoder er specifik for JavaScript

heldigvis afslører denne opførsel kun sig selv, hvis der bruges et overordnet felt i den overordnede konstruktør., Så kan det være svært at forstå, hvad der foregår, så vi forklarer det her.

Hvis det bliver et problem, kan man rette det ved hjælp af metoder eller getters / setters i stedet for felter.

Super: internals,]

avanceret information

Hvis du læser vejledningen for første gang – kan dette afsnit springes over.

det handler om de interne mekanismer bag arv og super.

lad os komme lidt dybere under hætten på super. Vi vil se nogle interessante ting undervejs.,

først at sige, fra alt det, vi har lært indtil nu, er det umuligt for super at arbejde overhovedet!

Ja, lad os spørge os selv, hvordan det skal teknisk fungere? Når en objektmetode kører, får den det aktuelle objekt som this. Hvis vi kalder super.method(), skal motoren hente method fra prototypen på det aktuelle objekt. Men hvordan?

opgaven kan virke enkel, men det er det ikke., Motoren kender det aktuelle objekt this, så det kunne få den forælder method som this.__proto__.method. Desværre fungerer en sådan “naiv” løsning ikke.

lad os demonstrere problemet. Uden klasser, ved hjælp af almindelige objekter af hensyn til enkelhed.

Du kan springe denne del over og gå nedenfor til ] underafsnit, hvis du ikke vil vide detaljerne. Det skader ikke. Eller læs videre, hvis du er interesseret i at forstå tingene i dybden.

i eksemplet nedenfor, rabbit.__proto__ = animal., Lad os nu prøve: i rabbit.eat() vi vil kalde animal.eat() hjælp this.__proto__:

Ved linje (*) vi tage eat fra prototype (animal), og kalder det i forbindelse med det aktuelle objekt. Bemærk, at .call(this) er vigtig her, fordi en simpel this.__proto__.eat() ville udføre forælder eat i forbindelse med prototypen, ikke det aktuelle objekt.,

og i koden ovenfor fungerer den faktisk som beregnet: Vi har den korrekte alert.

lad os nu tilføje endnu et objekt til kæden. Vi får se, hvordan tingene går i stykker:

koden fungerer ikke længere! Vi kan se fejlen ved at forsøge at ringe longEar.eat().

det er måske ikke så indlysende, men hvis vi sporer longEar.eat() opkald, så kan vi se hvorfor. I begge linier (*) og (**) værdi this er det aktuelle objekt (longEar)., Det er vigtigt: alle objektmetoder får det aktuelle objekt som this, ikke en prototype eller noget.

Her er et billede af, hvad der sker:

problemet kan ikke løses ved brug af this alene.

]

for at levere løsningen tilføjer JavaScript endnu en speciel intern egenskab til funktioner: ].,

Når en funktion er angivet som en klasse-eller objektmetode, bliver dens ] ejendom det objekt.

derefter super bruger det til at løse den overordnede prototype og dens metoder.

lad os se, hvordan det fungerer, først med almindelige objekter:

metoder er ikke “gratis”

Som vi har kendt før, er funktioner generelt “gratis”, ikke bundet til objekter i JavaScript. Så de kan kopieres mellem objekter og kaldes med en anden this.,

selve eksistensen af ] overtræder dette princip, fordi metoder husker deres objekter. ] kan ikke ændres, så denne binding er for evigt.

det eneste sted på det sprog, hvor ] bruges-er super. Så hvis en metode ikke bruger super, kan vi stadig betragte det som gratis og kopiere mellem objekter. Men med super kan tingene gå galt.,

Her er demo af en forkert super resultat efter kopiering:

En opfordring til at tree.sayHi() viser “jeg er et dyr”. Absolut forkert.

grunden er enkel:

Her er et diagram over hvad der sker:

Metoder, der ikke fungerer egenskaber

forskellen kan ikke være afgørende for os, men det er vigtigt for JavaScript.

i eksemplet nedenfor anvendes en ikke-metodesynta.til sammenligning., ] ejendommen ikke er fastsat, og den arv, der ikke virker:

Oversigt

  1. for At udvide en klasse: class Child extends Parent
    • , Som betyder Child.prototype.__proto__ vil Parent.prototype, så metoder, der er nedarvet.
  2. Når du tilsidesætter en konstruktør:
    • skal Vi kalde forælder constructor som super() i Child constructor, før du bruger this.,
  3. Når tvingende anden metode:
    • Vi kan bruge super.method() i Child metode til at ringe Parent metode.
  4. interne:
    • metoder husker deres klasse/objekt i det interne ] ejendom. Sådan løser super overordnede metoder.
    • så det er ikke sikkert at kopiere en metode med super fra et objekt til et andet.,

:

  • Pil funktioner, der ikke har deres egen this eller super, så de gennemsigtigt passer ind i den omgivende kontekst.