Klasse-overerving is een manier voor een klasse om een andere klasse uit te breiden.
zodat we nieuwe functionaliteit bovenop de bestaande kunnen creëren.
De “extends” zoekwoord
Laten we zeggen dat we de klasse Animal
:
Hier is hoe we kunnen vertegenwoordigen animal
object en Animal
class grafisch:
…En we willen het maken van een andere class Rabbit
.,
aangezien konijnen dieren zijn, moet Rabbit
klasse gebaseerd zijn op Animal
, toegang hebben tot diermethoden, zodat konijnen kunnen doen wat “generieke” dieren kunnen doen.
de syntaxis om een andere klasse uit te breiden is: class Child extends Parent
.
we maken class Rabbit
erft van Animal
:
Object Rabbit
class toegang zowel tot Rabbit
methoden, zoals rabbit.hide()
, en ook Animal
methoden, zoals rabbit.run()
.,
intern, extends
trefwoord werkt met behulp van de goede oude prototype mechanica. Het stelt Rabbit.prototype.]
in tot Animal.prototype
. Dus als een methode niet gevonden wordt in Rabbit.prototype
, dan neemt JavaScript deze van Animal.prototype
.,
bijvoorbeeld, om rabbit.run
methode, de motor controleert (bottom-up op de foto):
Als we kunnen herinneren uit het hoofdstuk Native prototypes, JavaScript zelf gebruikt prototypal erfenis ingebouwde objecten. Bijvoorbeeld Date.prototype.]
is Object.prototype
. Daarom hebben datums toegang tot generieke objectmethoden.,
extends
Class syntaxis maakt het mogelijk om niet alleen een klasse op te geven, maar elke expressie na extends
.
bijvoorbeeld, een functieaanroep die de bovenliggende klasse genereert:
function f(phrase) { return class { sayHi() { alert(phrase); } };}class User extends f("Hello") {}new User().sayHi(); // Hello
hier class User
erft van het resultaat of f("Hello")
.,
dat kan nuttig zijn voor geavanceerde programmeerpatronen wanneer we functies gebruiken om klassen te genereren, afhankelijk van vele omstandigheden en ervan kunnen erven.
overschrijven van een methode
laten we nu naar voren gaan en een methode overschrijven. Standaard worden alle methoden die niet zijn gespecificeerd in class Rabbit
direct” zoals is”genomen van class Animal
.,
maar als we onze eigen methode specificeren in Rabbit
, zoals stop()
dan zal het in plaats daarvan worden gebruikt:
class Rabbit extends Animal { stop() { // ...now this will be used for rabbit.stop() // instead of stop() from class Animal }}
meestal willen we niet volledig vervangen een bovenliggende methode, maar eerder om er bovenop te bouwen om de functionaliteit te tweaken of uit te breiden. We doen iets in onze methode, maar noemen de bovenliggende methode voor / na het of in het proces.
klassen geven hiervoor "super"
sleutelwoord.
-
super.method(...)
om een bovenliggende methode aan te roepen., -
super(...)
om een bovenliggende constructor aan te roepen (alleen binnen onze constructor).
bijvoorbeeld, laat ons konijn autohide wanneer gestopt:
nu Rabbit
heeft de stop
methode die de ouder super.stop()
aanroept in het proces.
overschrijven constructor
met constructors wordt het een beetje lastig.
tot nu toe had Rabbit
geen eigen constructor
.,
volgens de specificatie wordt als een klasse een andere klasse uitbreidt en geen constructor
heeft, het volgende “empty” constructor
gegenereerd:
class Rabbit extends Animal { // generated for extending classes without own constructors constructor(...args) { super(...args); }}
als we kunnen zien dat het in principe de ouder constructor
aanroept om alle argumenten door te geven. Dat gebeurt als we zelf geen constructeur schrijven.
laten we nu een aangepaste constructor toevoegen aan Rabbit
. Het specificeert de earLength
naast name
:
Whoops!, We hebben een fout. Nu kunnen we geen konijnen maken. Wat ging er mis?
het korte antwoord is:
- constructeurs in ervende klassen moeten
super(...)
aanroepen, en (!) doe dit voordat uthis
gebruikt.
… maar waarom? Wat is hier aan de hand? Inderdaad, de eis lijkt vreemd.
natuurlijk is er een verklaring. Laten we in details treden, zodat je echt begrijpt wat er aan de hand is.
in JavaScript is er een onderscheid tussen een constructorfunctie van een ervende klasse (de zogenaamde “afgeleide constructor”) en andere functies., Een afgeleide constructor heeft een speciale interne eigenschap ]:"derived"
. Dat is een speciaal intern etiket.
dat label beïnvloedt het gedrag met new
.
- wanneer een normale functie wordt uitgevoerd met
new
, wordt een leeg object gemaakt en toegewezen aanthis
. - maar als een afgeleide constructor draait, doet het dit niet. Het verwacht dat de ouder constructeur om dit werk te doen.,
dus een afgeleide constructor moet super
aanroepen om zijn moeder (basis) constructor uit te voeren, anders zal het object voor this
niet worden aangemaakt. En we krijgen een fout.
om de Rabbit
constructor te laten werken, moet het super()
aanroepen voordat this
wordt gebruikt, zoals hier:
overschrijdende klassenvelden: een lastige noot
Dit opmerking gaat ervan uit dat je een bepaalde ervaring hebt met lessen, misschien in andere programmeertalen.,
Het geeft een beter inzicht in de taal en verklaart ook het gedrag dat een bron van bugs kan zijn (maar niet vaak).
als je het moeilijk vindt om te begrijpen, ga dan gewoon verder, Lees verder en ga er later naar terug.
We kunnen niet alleen methoden overschrijven, maar ook klassenvelden.
hoewel, er is een lastig gedrag wanneer we toegang krijgen tot een overschreven veld in parent constructor, heel anders dan de meeste andere programmeertalen.,
beschouw dit voorbeeld:
hier, classRabbit
breidtAnimal
uit en overschrijftname
veld met zijn eigen waarde.
er is geen eigen constructor in Rabbit
, dus Animal
constructor wordt aangeroepen.
interessant is dat in beide gevallen: new Animal()
en new Rabbit()
, de alert
in de regel (*)
toont animal
.,
met andere woorden, bovenliggende constructor gebruikt altijd zijn eigen veldwaarde, niet de overschreven waarde.
Wat is er vreemd aan?
als het nog niet duidelijk is, vergelijk dan met methoden.
Hier is dezelfde code, maar in plaats van this.name
veld dat we this.showName()
methode:
let op: Nu is de uitvoer anders.
en dat is wat we natuurlijk verwachten. Wanneer de bovenliggende constructor wordt aangeroepen in de afgeleide klasse, gebruikt deze de overschrijdende methode.
… maar voor klasse velden is het niet zo. Zoals gezegd, de ouder constructor gebruikt altijd het ouder veld.,
Waarom is er het verschil?
wel, de reden is in de veld initialisatie volgorde. Het class-veld wordt geïnitialiseerd:
- voor de constructor voor de basisklasse (die niets uitbreidt),
- onmiddellijk na
super()
voor de afgeleide klasse.
in ons geval is Rabbit
de afgeleide klasse. Er zit geen constructor()
in. Zoals eerder gezegd, is dat hetzelfde als wanneer er een lege constructor was met alleen super(...args)
.,
So, new Rabbit()
roept super()
aan, waardoor de bovenliggende constructor wordt uitgevoerd, en (volgens de regel voor afgeleide klassen) pas nadat de klassenvelden zijn geïnitialiseerd. Op het moment dat de bovenliggende constructor wordt uitgevoerd, zijn er nog geen Rabbit
klassenvelden, daarom worden Animal
velden gebruikt.
Dit subtiele verschil tussen velden en methoden is specifiek voor JavaScript
Gelukkig wordt dit gedrag alleen zichtbaar als een overschreven veld wordt gebruikt in de bovenliggende constructor., Dan is het misschien moeilijk te begrijpen wat er aan de hand is, dus we leggen het hier uit.
als het een probleem wordt, kan men het oplossen met behulp van methoden of getters / setters in plaats van velden.
Super: internals,]
Als u de tutorial voor de eerste keer leest – deze sectie kan worden overgeslagen.
het gaat over de interne mechanismen achter overerving en super
.
laten we wat dieper gaan onder de motorkap van super
. We zullen onderweg interessante dingen zien.,
om eerst te zeggen, van alles wat we tot nu toe geleerd hebben, is het onmogelijk voor super
om überhaupt te werken!
Ja, inderdaad, laten we ons afvragen, hoe het technisch zou moeten werken? Als een objectmethode wordt uitgevoerd, krijgt deze het huidige object als this
. Als we super.method()
aanroepen, dan moet de engine de method
ophalen van het prototype van het huidige object. Maar hoe?
de taak lijkt misschien eenvoudig, maar dat is het niet., De engine kent het huidige object this
, dus het zou de ouder method
kunnen krijgen als this.__proto__.method
. Helaas, zo ‘ n “naïeve” oplossing zal niet werken.
laten we het probleem demonstreren. Zonder klassen, met behulp van gewone objecten omwille van de eenvoud.
u kunt dit deel overslaan en hieronder naar de subsectie ]
gaan als u de details niet wilt weten. Dat kan geen kwaad. Of lees verder als je geïnteresseerd bent in het begrijpen van dingen in de diepte.
in het voorbeeld hieronder, rabbit.__proto__ = animal
., Laten we nu eens proberen: in rabbit.eat()
roepen we animal.eat()
, met this.__proto__
:
Op de regel (*)
nemen we eat
van de prototype (animal
) en noem het in de context van het huidige object. Houd er rekening mee dat .call(this)
hier belangrijk is, omdat een eenvoudige this.__proto__.eat()
ouder eat
zou uitvoeren in de context van het prototype, niet het huidige object.,
en in de bovenstaande code werkt het eigenlijk zoals bedoeld: we hebben de juiste alert
.
laten we nu nog een object aan de keten toevoegen. We zullen zien hoe dingen breken:
de code werkt niet meer! We kunnen de fout zien bij het aanroepen van longEar.eat()
.
het is misschien niet zo voor de hand liggend, maar als we longEar.eat()
aanroepen, dan kunnen we zien waarom. In beide regels is (*)
en (**)
de waarde van this
het huidige object (longEar
)., Dat is essentieel: alle object methoden krijgen het huidige object als this
, geen prototype of iets dergelijks.
Hier is het beeld van wat er gebeurt:
het probleem kan niet worden opgelost door this
alleen te gebruiken.
]
om de oplossing te bieden, voegt JavaScript nog een speciale interne eigenschap toe voor functies: ]
.,
wanneer een functie is opgegeven als een klasse-of objectmethode, wordt de eigenschap ]
dat object.
dan super
gebruikt het om het ouder prototype en zijn methoden op te lossen.
laten we eens kijken hoe het werkt, eerst met gewone objecten:
methoden zijn niet “vrij”
zoals we eerder hebben gekend, zijn functies over het algemeen “vrij”, niet gebonden aan objecten in JavaScript. Dus ze kunnen worden gekopieerd tussen objecten en aangeroepen met een andere this
.,
het bestaan van ]
schendt dit principe, omdat methoden hun objecten onthouden. ]
kan niet worden gewijzigd, dus deze binding is voor altijd.
de enige plaats in de taal waar ]
wordt gebruikt – is super
. Dus als een methode super
niet gebruikt, dan kunnen we het nog steeds vrij beschouwen en tussen objecten kopiëren. Maar met super
kan er iets mis gaan.,
Hier is de demo van een verkeerde super
resultaat na het kopiëren:
een aanroep naar tree.sayHi()
toont”ik ben een dier”. Zeker fout.
De reden is simpel:
Hier is het schema van wat er gebeurt:
Methoden, niet functie-eigenschappen
Het verschil kan een niet-essentieel zijn voor ons, maar het is belangrijk voor JavaScript.
in het voorbeeld hieronder wordt een niet-methodesyntaxis gebruikt voor vergelijking., ]
eigenschap is niet ingesteld en de overerving werkt niet:
samenvatting
- om een klasse uit te breiden:
class Child extends Parent
:- Dat betekent
Child.prototype.__proto__
zalParent.prototype
, dus methoden worden overgeërfd.
- Dat betekent
- bij het overschrijven van een constructor:
- moeten we bovenliggende constructor aanroepen als
super()
inChild
constructor alvorensthis
te gebruiken.,
- moeten we bovenliggende constructor aanroepen als
- bij het overschrijven van een andere methode:
- we kunnen
super.method()
gebruiken in eenChild
methode omParent
methode aan te roepen.
- we kunnen
- Internals:
- methoden onthouden hun klasse / object in de interne
]
eigenschap. Dat is hoesuper
bovenliggende methoden oplost. - het is dus niet veilig om een methode met
super
van het ene object naar het andere te kopiëren.,
- methoden onthouden hun klasse / object in de interne
ook:
- Pijlfuncties hebben geen eigen
this
ofsuper
, dus ze passen transparant in de omringende context.
© 2021 Tombouctou
Thema gemaakt door Anders Noren — Boven ↑
Geef een reactie