Klasse arv er en måte for en klasse å forlenge en annen klasse.

Slik at vi kan lage ny funksjonalitet på toppen av de eksisterende.

«strekker seg» søkeord

La oss si at vi har klasse Animal:

Her er hvordan vi kan representere animal objekt og Animal klasse grafisk:

…Og vi ønsker å skape en annen class Rabbit.,

Som kaniner er dyr, Rabbit klassen bør være basert på Animal, har tilgang til dyr metoder, slik at kaninene kan gjøre hva «generisk» dyr kan gjøre.

syntaksen til å forlenge en annen klasse er: class Child extends Parent.

La oss skape class Rabbit som arver fra Animal:

Objekt av Rabbit klassen har tilgang både til Rabbit metoder, som for eksempel rabbit.hide(), og også til Animal metoder, som for eksempel rabbit.run().,

Internt, extends søkeord fungerer ved hjelp av den gode gamle prototypen mekanikk. Det setter Rabbit.prototype.] til Animal.prototype. Så, hvis en metode ikke finnes i Rabbit.prototype, JavaScript tar det fra Animal.prototype.,

For eksempel, for å finne rabbit.run metode, motoren kontroller (bottom-up på bildet):

Som vi kan hente fra kapittel Native prototyper, JavaScript selv bruker prototypal arv for innebygde objekter. E. g. Date.prototype.] er Object.prototype. Det er derfor datoer har tilgang til generisk objekt-metoder.,

Noen uttrykk er tillatt etter extends

Klasse syntaks gjør det mulig å angi ikke bare en klasse, men noen uttrykk etter extends.

For eksempel, en funksjon samtale som 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 av f("Hello").,

Som kan være nyttig for avansert programmering mønstre når vi bruker funksjoner for å generere klasser avhengig av mange forhold og kan arve fra dem.

Tvingende en metode

la oss Nå gå videre og overstyre en metode. Som standard er alle metoder som ikke er spesifisert i class Rabbit er tatt direkte «som den er», fra class Animal.,

Men hvis vi spesifiserer vår egen metode i Rabbit, for eksempel stop() så vil det bli brukt i stedet:

– >

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

Vanligvis vil vi ikke helt erstatte en overordnet metode, men heller å bygge på toppen av det å justere eller utvide funksjonaliteten. Vi gjør noe i vår metode, men kaller den overordnede metoden før/etter det, eller i den prosessen.

Klasser gir "super" søkeord for det.

  • super.method(...) for å ringe en overordnet metode.,
  • super(...) for å ringe en forelder constructor (inne i våre constructor bare).

For eksempel, la vår kanin autohide når stoppet:

Rabbit har stop metode som kaller den overordnede super.stop() i prosessen.

Overordnede constructor

Med konstruktører det blir litt vanskelig.

Før nå, Rabbit ikke har sin egen constructor.,

i Henhold til spesifikasjon, hvis en klasse strekker seg en annen klasse og har ingen constructor, then the following «tom» constructor er generert:

– >

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

Som vi kan se, det er i utgangspunktet kaller den overordnede constructor passerer det alle argumenter. Det skjer hvis vi ikke skrive en konstruktør av våre egne.

la oss Nå legge til en egendefinert constructor til Rabbit. Det vil spesifisere earLength i tillegg til name:

Whoops!, Vi har fått en feil. Nå kan vi ikke lage kaniner. Hva gikk galt?

Det korte svaret er:

  • Konstruktører i klasser som arver må ringe super(...), og (!) gjøre det før du bruker this.

…Men hvorfor? Hva skjer her? Ja, kravet virker merkelig.

selvfølgelig, det er en forklaring. La oss komme inn i detaljer, slik at du virkelig forstår hva som skjer.

I JavaScript, er det et skille mellom en konstruktør funksjon av en klasse som arver (såkalte «avledet constructor») og andre funksjoner., En avledet constructor har en spesiell intern eiendel ]:"derived". Det er en spesiell intern etiketten.

At etiketten påvirker dens atferd med new.

  • Når en vanlig funksjon er gjennomført med new, det skaper et tomt objekt og tilordner den til this.
  • Men når en avledet constructor går, det gjør ikke denne. Det forventer de overordnede konstruktør å gjøre denne jobben.,

Så en avledet konstruktøren må ringe super for å utføre sine foreldre (base) konstruktør, ellers objekt for this vil ikke bli opprettet. Og vi vil få en feilmelding.

For Rabbit constructor til å fungere, er det behov for å ringe super() før du bruker this, som her:

Overordnede klasse felt: en vanskelig merk

Avansert merk

merk Dette forutsetter at du har en viss erfaring med klasser, kanskje i andre programmeringsspråk.,

Det gir bedre innsikt i språket og som også forklarer atferd som kan være en kilde til feil (men ikke veldig ofte).

Hvis du synes det er vanskelig å forstå, bare gå på, kan du fortsette å lese, og deretter gå tilbake til det litt senere.

Vi kan overstyre ikke bare metoder, men også klasse felt.

Selv om det er en vanskelig atferd når vi tilgang til et overstyres feltet i overordnede konstruktør, ganske forskjellig fra de fleste andre programmeringsspråk.,

se på dette eksempelet:

Her, klasse Rabbit strekker Animal og overstyrer name felt med sin egen verdi.

Det er ingen egen konstruktør Rabbit slik Animal constructor er kalt.

Hva er interessant er at i begge tilfeller: new Animal() og new Rabbit(), alert i linje (*) viser animal.,

med andre ord, foreldre constructor alltid bruker sin egen feltet verdi, ikke overstyres ett.

Hva er rart med det?

Hvis det ikke er klart ennå, kan du sammenligne med metoder.

Her er den samme koden, men i stedet for this.name – feltet vi kaller this.showName() metode:

merk: nå er det utgang er forskjellige.

Og det er det vi naturlig kan forvente. Når foreldrene constructor kalles i avledet klasse, bruker den overstyres metode.

…Men for klasse felt er det ikke slik. Som sagt, den overordnede constructor alltid bruker den overordnede feltet.,

Hvorfor er det forskjell?

Vel, grunnen er i feltet initialisering for. Klassen feltet er initialisert:

  • Før konstruktør for base-klasse (som ikke forlenge noe som helst),
  • Umiddelbart etter super() for avledet klasse.

I vårt tilfelle, Rabbit er avledet klasse. Det er ingen constructor() i det. Som sagt tidligere, det er det samme som om det var en tom konstruktør med bare super(...args).,

Så, new Rabbit() anrop super(), og dermed utføre den overordnede constructor, og (per regelen om avledet klasser) etter at sin klasse felt er initialisert. På tidspunktet for den overordnede constructor kjøring, det er ingen Rabbit klasse felt ennå, det er grunnen til Animal felt er brukt.

Denne subtile forskjellen mellom felter og metoder som er spesifikke for JavaScript

Heldigvis, dette problemet bare åpenbarer seg hvis en overstyres feltet er brukt i den overordnede constructor., Da kan det bli vanskelig å forstå hva som skjer, så vi forklare det her.

Hvis det blir et problem, kan man løse det ved hjelp av metoder eller getters/bidra i stedet for feltene.

Super: innvendig ]

Avansert informasjon

Hvis du leser veiledningen for første gang – i denne delen kan hoppes over.

Det er om det interne mekanismer bak arven og super.

La oss få en litt dypere under panseret på super. Vi får se noen interessante ting underveis.,

Første til å si, fra alt det vi har lært til nå, er det umulig for super for å fungere i det hele tatt!

Ja, ja, la oss spørre oss selv, hvordan det skal teknisk arbeid? Når et objekt metoden går, det blir gjeldende objekt som this. Hvis vi kaller super.method() deretter motoren behov for å få method fra prototypen på det aktuelle objektet. Men hvordan?

oppgaven kan virke enkelt, men det er det ikke., Motoren vet det aktuelle objektet this, slik at det kan få den overordnede method som this.__proto__.method. Dessverre, slike en «naiv» løsningen ikke vil fungere.

La oss demonstrere problemet. Uten klasser, ved hjelp av vanlig objekter for enkelhets skyld.

Du kan hoppe over denne delen og gå under til ] ledd dersom du ikke ønsker å vite detaljene. Det vil ikke skade. Eller les videre hvis du er interessert i å forstå ting i dybden.

I eksempelet nedenfor, rabbit.__proto__ = animal., La oss nå prøve: i rabbit.eat() vi kaller animal.eat(), ved hjelp av this.__proto__:

På linje (*) vi ta eat fra prototypen (animal) og kaller det i sammenheng med det aktuelle objektet. Vær oppmerksom på at .call(this) som er viktig her, fordi en enkel this.__proto__.eat() ville utføre overordnet eat i sammenheng med prototypen, ikke gjeldende objekt.,

i koden ovenfor er det faktisk fungerer etter hensikten: vi har den riktige alert.

la oss Nå legge til ett objekt til kjeden. Vi får se hvordan ting pause:

– koden ikke fungerer lenger! Vi kan se feilen, prøver du å ringe longEar.eat().

Det er kanskje ikke så opplagt, men hvis vi spore longEar.eat() samtale, så kan vi se hvorfor. I begge linjer (*) og (**) verdi this er gjeldende objekt (longEar)., Det er viktig: alle objekt-metoder få det aktuelle objektet som this, ikke en prototype eller noe.

Her er bilde av hva som skjer:

problemet ikke kan løses ved hjelp av this alene.

]

for Å gi løsningen, JavaScript legger en mer spesielle interne eiendom for funksjoner: ].,

Når en funksjon er definert som en klasse eller objekt metode, dets ] eiendommen blir dette objektet.

super bruker det til å løse de overordnede prototype og metoder.

La oss se hvordan det fungerer, først med vanlig objekter:

Metodene er ikke «gratis»

Som vi har kjent før, generelt funksjonene er «gratis», ikke er bundet til objekter i JavaScript. Slik at de kan kopieres mellom objekter og kalles med en annen this.,

selve eksistensen av ] bryter med dette prinsippet, fordi metodene som husker sine objekter. ] kan ikke endres, så denne bindingen er for alltid.

Det eneste stedet i språk der ] brukes – er super. Så, hvis en metode ikke bruk super, så kan vi likevel vurdere det gratis og kopiere mellom objekter. Men med super ting kan gå galt.,

Her er en demo av en feil super resultat etter kopiering:

En-anrop til tree.sayHi() viser «jeg er et dyr». Definitivt feil.

grunnen er enkel:

Her er bildet av hva som skjer:

– Metoder, ikke funksjonen egenskaper

forskjellen kan være » ikke-viktig for oss men det er viktig for JavaScript.

I eksemplet nedenfor er en ikke-metoden er syntaksen som brukes for sammenligning., ] eiendel er ikke satt, og arv ikke fungerer:

Oppsummering

– >

  1. for Å utvide klassen: class Child extends Parent:
    • Som betyr Child.prototype.__proto__ skal Parent.prototype, slik at metodene er arvet.
  2. Når tvingende en konstruktør:
    • Vi må ringe foreldrene konstruktør som super() i Child constructor før du bruker this.,
  3. Når tvingende en annen metode:
    • kan Vi bruke super.method() i Child metode for å ringe Parent metode.
  4. Internals:
    • Metoder huske sin klasse/objekt i den interne ] eiendom. Det er hvordan super løser overordnede metoder.
    • Så det er ikke trygt å kopiere en metode med super fra ett objekt til et annet.,

Også:

  • Pil-funksjonene som ikke har egne this eller super, transparent slik at de passer inn i det omkringliggende kontekst.