Funksjon Prototype Eiendom

De prototype Egenskapen er et objekt som er opprettet av JavaScript for hver Funksjon() forekomst. Spesifikt kobler den objektobjekter som er opprettet med ny søkeord tilbake til konstruktørfunksjonen som opprettet dem. Dette er gjort slik at forekomster kan dele eller arve, vanlige metoder og egenskaper. Det er viktig at delingen skjer under eiendomsoppslag. Husk fra den første artikkelen at hver gang du ser opp eller får tilgang til en eiendom på et objekt, blir eiendommen søkt på objektet, så vel som prototypekjeden.

Et prototypobjekt opprettes for hver funksjon, uavhengig av om du har tenkt å bruke denne funksjonen som en konstruktør.

I den følgende koden konstruerer jeg en matrise fra Array () konstruktør, og deretter påkaller jeg bli med() metode.

Eksempel: sample118.html

 

De bli med() Metoden er ikke definert som en egenskap av myArray objekt-forekomst, men på en eller annen måte har vi tilgang til bli med() som om det var. Denne metoden er definert et sted, men hvor? Vel, det er definert som en eiendom av Array () konstruktørens prototypeegenskap. Siden bli med() finnes ikke i arrayobjektet, ser JavaScript opp prototypekjeden for en metode som kalles bli med().

Ok, så hvorfor er ting gjort på denne måten? Egentlig handler det om effektivitet og gjenbruk. Hvorfor bør alle array-forekomster opprettet fra array constructor-funksjonen ha en unikt definert bli med() metode når bli med() Fungerer alltid på samme måte? Det er mer fornuftig for alle arrays å utnytte det samme bli med() funksjon uten å måtte opprette en ny forekomst av funksjonen for hver array-forekomst.

Denne effektiviteten vi snakker om er alt mulig på grunn av prototype eiendom, prototype kobling og prototype oppslagskjeden. I denne artikkelen bryter vi ned disse ofte forvirrende egenskapene til prototypisk arv. Men sannheten blir fortalt, du ville bli bedre ved å bare huske mekanikken til hvordan kjeden hierarkiet egentlig fungerer. Henvis til den første artikkelen hvis du trenger en oppdatering på hvordan eiendomsverdiene er løst.


Hvorfor bry deg om prototype Eiendom?

Du burde bryr deg om prototype eiendom av fire grunner.

Årsak 1

Den første grunnen er at prototypegenskapen brukes av de opprinnelige konstruktørfunksjonene (Gjenstand(), Array (), Funksjon(), etc.) for å tillate konstruktørinstanser å arve egenskaper og metoder. Det er mekanismen som JavaScript selv bruker for å tillate objektet forekomster å arve egenskaper og metoder fra konstruktørfunksjonens prototype eiendom. Hvis du vil forstå JavaScript bedre, må du forstå hvordan JavaScript selv utnytter prototype gjenstand.

Årsak 2

Når du oppretter brukerdefinerte konstruktørfunksjoner, kan du orkestre arv på samme måte som JavaScript-innfødte objekter gjør. Men først må du lære hvordan det fungerer.

Årsak 3

Du kan virkelig ikke likne prototypisk arv eller foretrekker et annet mønster for gjenstand for arv, men virkeligheten er at en dag må du kanskje redigere eller administrere andres kode som trodde prototypal arv var bienes knær. Når dette skjer, bør du være oppmerksom på hvordan prototypisk arv fungerer, samt hvordan den kan replikeres av utviklere som bruker tilpassede konstruktørfunksjoner.

Årsak 4

Ved å bruke prototypal arv, kan du opprette effektive objektfelter som alle bruker de samme metodene. Som allerede nevnt, ikke alle array objekter, som er forekomster av Array () konstruktør, trenger sin egen bli med() metoder. Alle forekomster kan utnytte det samme bli med() metode fordi metoden er lagret i prototypekjeden.

Prototype er standard på alle Funksjon() forekomster

Alle funksjoner er opprettet fra a Funksjon() konstruktør, selv om du ikke direkte påkaller Funksjon() konstruktør (var add = ny funksjon ('x', 'y', 'return x + z');) og i stedet bruk bokstavelig notasjon (var add = funksjon (x, y) retur x + z;).

Når en funksjonsinstans er opprettet, blir den alltid gitt a prototype eiendom, som er en tom gjenstand. I den følgende prøven definerer vi en funksjon som heter myFunction og åpner deretter prototype eiendom som er rett og slett en tom gjenstand.

Eksempel: sample119.html

 

Pass på at du helt forstår at prototypegenskapen kommer fra Funksjon() konstruktør. Det er bare en gang vi har tenkt å bruke vår funksjon som en brukerdefinert konstruktørfunksjon som prototypegenskapen er leveransert, men dette endrer ikke det faktum at Funksjon() Konstruktøren gir hver instans en prototypegenskap.


Standaren prototype Eiendommen er en Gjenstand() Gjenstand

Alt dette prototype snakk kan bli litt tungt. Sannelig, prototype er bare en tom objektegenskap kalt "prototype" opprettet bak kulissene ved JavaScript og gjort tilgjengelig ved å påkalle Funksjon() konstruktør. Hvis du skulle gjøre det manuelt, ville det se slik ut:

Eksempel: sample120.html

 

Faktisk fungerer denne prøvekoden egentlig fint, egentlig bare duplisere hva JavaScript allerede gjør.

Verdien av en prototypegenskap kan settes til noen av de komplekse verdiene (objekter) som er tilgjengelige i JavaScript. JavaScript vil ignorere noen prototypegenskaper satt til en primitiv verdi.


Instanser laget av en konstruktørfunksjon er knyttet til konstruktørens prototype Eiendom

Mens det bare er et objekt, prototype er spesielt fordi prototypekjeden kobler hver forekomst til sin konstruktørfunksjons prototypeegenskap. Dette betyr at når et objekt opprettes fra en konstruktørfunksjon ved hjelp av ny søkeord (eller når et objektomslag er opprettet for en primitiv verdi), legger den til en skjult lenke mellom objektet forekomst opprettet og prototypegenskapen til konstruktørfunksjonen som ble brukt til å lage den. Denne lenken er kjent innenfor forekomsten som __proto__ (selv om det bare er utsatt / støttet via kode i Firefox 2+, Safari, Chrome og Android). JavaScript slår sammen dette i bakgrunnen når en konstruktørfunksjon er påkalt, og den er denne lenken som gjør at prototypekjeden kan være, vel, en kjede. I den følgende prøven legger vi en eiendom til den innfødte Array () konstruktører prototype, som vi kan få tilgang til fra en Array () eksempel ved bruk av __proto__ eiendom sett på den forekomsten.

Eksempel: sample121.html

 

Siden tilgang __proto__ er ikke en del av den offisielle ECMA-standarden, det er en mer universell måte å spore linken fra en gjenstand til prototypen objektet den arver, og det er ved å bruke konstruktør eiendom. Dette er demonstrert i følgende prøve.

Eksempel: sample122.html

 

I dette eksemplet er foo Egenskapen er funnet innenfor prototypeobjektet. Du må innse at dette kun er mulig på grunn av sammenhengen mellom forekomsten av Array () og Array () konstruktør prototype objekt (Array.prototype). For å si det enkelt, myArray .__ proto__ (eller myArray.constructor.prototype) referanser Array.prototype.


Siste stopp i prototype Kjeden er Object.prototype

Siden prototypegenskapen er en gjenstand, er siste stopp i prototypekjeden eller oppslaget på Object.prototype. I koden som følger oppretter jeg myArray, som er en tom rekkefølge. Jeg forsøker da å få tilgang til en eiendom på myArray som ennå ikke er definert, engasjerer prototype oppslagskjeden. De myArray objektet undersøkes for foo-eiendommen. Å være fraværende, er eiendommen søkt etter Array.prototype, men det er heller ikke der. Så det siste stedet JavaScript ser ut er Object.prototype. Fordi det ikke er definert i noen av de tre objektene, er eiendommen udefinert.

Eksempel: sample123.html

 

Legg merke til at kjeden stoppet med Object.prototype. Det siste stedet vi så etter foo var Object.prototype.

Forsiktig! Alt som er lagt til Object.prototype vil dukke opp i en for-i-loop.


De prototype Kjede returnerer den første eiendomsmatchen den finner i kjeden

Som omfanget kjeden, den prototype kjeden vil bruke den første verdien den finner under kjedeoppslaget.

Endre forrige kodeeksempel, hvis vi la samme verdi til Object.prototype og Array.prototype objekter, og forsøkte å få tilgang til en verdi på en array-forekomst, ville verdien returnert være fra Array.prototype gjenstand.

Eksempel: sample124.html

 

I denne prøven er foo verdien på Array.prototype.foo er skygging, eller maskering, foo verdi funnet på Object.prototype.foo. Bare husk at oppslaget slutter når eiendommen er funnet i kjeden, selv om samme eiendomsnavn også brukes lenger opp i kjeden.


Bytte ut prototype Egenskap med et nytt objekt fjerner standardkonstruksjonsegenskapen

Det er mulig å erstatte standardverdien av a prototype eiendom med en ny verdi. Det vil imidlertid eliminere standardkonstruktøregenskapen som finnes i "pre-made" prototype objekt med mindre du spesifiserer en manuelt.

I koden som følger oppretter vi en foo konstruktør funksjon, erstatte prototype eiendom med en ny tom objekt, og verifiser at byggherreegenskapen er ødelagt (det refererer nå til de mindre nyttige Gjenstand prototype).

Eksempel: sample125.html

 

Hvis du har tenkt å erstatte standard prototype eiendom (vanlig med noen JS OOP-mønstre) satt opp av JavaScript, bør du koble sammen en byggherre som refererer til konstruktørfunksjonen. I den følgende prøven endrer vi vår tidligere kode slik at konstruktør Egenskapen vil igjen gi en referanse til den riktige konstruktørfunksjonen.

Eksempel: sample126.html

 

Forekomster som erverver egenskaper fra prototype Vil alltid få de nyeste verdiene

Prototypegenskapen er dynamisk i den forstand at forekomster alltid vil få den nyeste verdien fra prototypen, uansett når den ble instansiert, endret eller vedlagt. I koden som følger oppretter vi en foo byggherre, legg til eiendommen x til prototype, og opprett deretter en forekomst av Foo () oppkalt FooInstance. Deretter logger vi verdien av x. Da oppdaterer vi prototypeværdien til x og logger den igjen for å finne ut at vår forekomst har tilgang til den nyeste verdien som finnes i prototype gjenstand.

Eksempel: sample127.html

 

Gitt hvordan oppslagskjeden virker, bør denne oppførselen ikke være så overraskende. Hvis du lurer på, fungerer dette det samme uansett om du bruker standardinnstillingen prototype objekt eller overstyr det med din egen. I neste prøve erstatter jeg standardinnstillingen prototype protestere mot å demonstrere dette faktum.

Eksempel: sample128.html

 

Bytte ut prototype Eiendom med nytt objekt oppdager ikke tidligere forekomster

Du tror kanskje at du kan erstatte prototype eiendom helt til enhver tid, og at alle forekomster vil bli oppdatert, men dette er ikke riktig. Når du oppretter en forekomst, blir den forekomsten knyttet til prototype Det ble myntet på tidspunktet for instantiering. Å oppgi et nytt objekt som prototypegenskapen, oppdaterer ikke forbindelsen mellom forekomster som allerede er opprettet og den nye prototype.

Men husk, som jeg tidligere nevnte, kan du oppdatere eller legge til til opprinnelig opprettet prototype objekt og disse verdiene forblir koblet til første instans (er).

Eksempel: sample129.html

 

Nøkkel ideen å ta bort her er at en prototype av objekter ikke skal erstattes med et nytt objekt når du begynner å lage forekomster. Å gjøre det vil resultere i tilfeller som har en lenke til forskjellige prototyper.

Brukerdefinerte byggere kan utnytte det samme prototype Arv som innfødte byggere

Forhåpentligvis på dette punktet i artikkelen, synker det i hvordan JavaScript selv utnytter prototype eiendom for arv (Array.prototype). Det samme mønsteret kan utnyttes når du lager ikke-innfødte, brukerdefinerte konstruktørfunksjoner. I den følgende prøven tar vi den klassiske Person objekt og etterligne mønsteret som JavaScript bruker for arv.

Eksempel: sample130.html

 

I denne koden, a Person() Konstruktørfunksjonen er opprettet. Vi legger da til egenskaper til prototype tilhører Person(), som kan arves av alle tilfeller. Du kan tydeligvis utnytte prototypekjeden i koden din på samme måte som JavaScript utnytter den til opprinnelige objektarv.

Som et godt eksempel på hvordan du kan utnytte dette, kan du opprette en konstruktørfunksjon hvis forekomster arver ben og våpen egenskaper hvis de ikke er oppgitt som parametere. I følgende eksempel, hvis Person() Konstruktøren sendes parametere, parametrene brukes som eksempelegenskaper, men hvis en eller flere parametere ikke er oppgitt, er det en tilbakebetaling. Disse forekomstegenskapene skygger eller maskerer de arvede egenskaper, og gir deg det beste fra begge verdener.

Eksempel: prøve131.html

 

Opprette Arvelighetskjeder (den opprinnelige hensikten)

Prototypal arv ble uttalt for å tillate arvskett som etterligner arvsmønstrene som finnes i tradisjonelle objektorienterte programmeringsspråk. For at et objekt skal arve fra et annet objekt i JavaScript, er alt du trenger å gjøre, å instantiere en forekomst av objektet du vil arve fra, og tilordne det til prototype Egenskapen til objektet som gjør arven.

I koden prøven som følger, Chef objekter (cody) arve fra Person(). Dette betyr at hvis en eiendom ikke finnes i en Chef objekt, vil det bli etterspurt på prototypen av funksjonen som ble opprettet Person() objekter. Å koble opp arven, alt du trenger å gjøre er å instansere en forekomst av Person() som verdien for Chef.prototype (Chef.prototype = ny person (); ).

Eksempel: sample132.html

 

Konklusjon

Alt vi gjorde i denne prøven, utnytte et system som allerede var på plass med de opprinnelige objektene. Tenk på det Person() er ikke ulikt standard Gjenstand() verdi for prototypegenskaper. Med andre ord, dette er akkurat hva som skjer når en prototype-egenskap, som inneholder standard tomt Gjenstand() verdi, ser til prototypen av konstruktørfunksjonen som er opprettet (Object.prototype) for arvede egenskaper.