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.,
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:
Nå 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 brukerthis
.
…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 tilthis
. - 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
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 ]
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.
Så 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
– >
- for Å utvide klassen:
class Child extends Parent
:- Som betyr
Child.prototype.__proto__
skalParent.prototype
, slik at metodene er arvet.
- Som betyr
- Når tvingende en konstruktør:
- Vi må ringe foreldrene konstruktør som
super()
iChild
constructor før du brukerthis
.,
- Vi må ringe foreldrene konstruktør som
- Når tvingende en annen metode:
- kan Vi bruke
super.method()
iChild
metode for å ringeParent
metode.
- kan Vi bruke
- Internals:
- Metoder huske sin klasse/objekt i den interne
]
eiendom. Det er hvordansuper
løser overordnede metoder. - Så det er ikke trygt å kopiere en metode med
super
fra ett objekt til et annet.,
- Metoder huske sin klasse/objekt i den interne
Også:
- Pil-funksjonene som ikke har egne
this
ellersuper
, transparent slik at de passer inn i det omkringliggende kontekst.
Legg igjen en kommentar