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.,

elke expressie is toegestaan na 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 constructoraanroept 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 u thisgebruikt.

… 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 aan this.
  • 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

geavanceerde 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,]

geavanceerde informatie

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

  1. om een klasse uit te breiden: class Child extends Parent:
    • Dat betekent Child.prototype.__proto__ zal Parent.prototype, dus methoden worden overgeërfd.
  2. bij het overschrijven van een constructor:
    • moeten we bovenliggende constructor aanroepen als super() in Child constructor alvorens thiste gebruiken.,
  3. bij het overschrijven van een andere methode:
    • we kunnen super.method() gebruiken in een Child methode om Parent methode aan te roepen.
  4. Internals:
    • methoden onthouden hun klasse / object in de interne] eigenschap. Dat is hoe super bovenliggende methoden oplost.
    • het is dus niet veilig om een methode met super van het ene object naar het andere te kopiëren.,

ook:

  • Pijlfuncties hebben geen eigenthis ofsuper, dus ze passen transparant in de omringende context.

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *