Prototype i JavaScript: det er pænt, men sådan fungerer det

Følgende fire linjer er nok til at forvirre de fleste JavaScript-udviklere:

Object instanceof Function//true
Object instanceof Object//true
Function instanceof Object//true
Function instanceof Function//true

Prototype i JavaScript er et af de mest forbløffende koncepter, men du kan ikke undgå det. Uanset hvor meget du ignorerer det, vil du støde på prototype-puslespillet i løbet af dit JavaScript-liv.

Så lad os se det i øjnene.

Startende med det grundlæggende er der følgende datatyper i JavaScript:

  1. udefineret
  2. nul
  3. nummer
  4. snor
  5. boolsk
  6. objekt

De første fem er primitive datatyper. Disse gemmer en værdi af deres type, såsom en boolsk, og kan være sand eller falsk .

Det sidste "objekt" er en referencetype, som vi kan beskrive som en samling af nøgleværdipar (men det er meget mere).

I JavaScript oprettes nye objekter ved hjælp af Object constructor- funktion (eller objekt-bogstavelig {}), der giver generiske metoder som toString()og valueOf().

Funktioner i JavaScript er specielle objekter, der kan kaldes . Vi laver dem og ved hjælp af funktionskonstruktorfunktionen (eller funktionslitteral). Det faktum, at disse konstruktører er objekter såvel som funktion, har altid forvirret mig, meget på samme måde som kyllingæg-gåden forvirrer alle.

Før jeg starter med prototyper, vil jeg præcisere, at der er to prototyper i JavaScript:

  1. prototype : Dette er et specielt objekt, der tildeles som egenskab for enhver funktion, du laver i JavaScript. Lad mig være klar her, den er allerede til stede for enhver funktion, du laver, men ikke obligatorisk for interne funktioner leveret af JavaScript (og funktion returneret af bind). Dette prototypeer det samme objekt, som peges på[[Prototype]](se nedenfor) af et nyoprettet objekt fra denne funktion (ved hjælp af newnøgleord).
  2. [[Prototype]]: Dette er en eller anden måde skjult egenskab på hvert objekt, der er adgang til af den kørende kontekst, hvis en eller anden ejendom, der læses på objektet, ikke er tilgængelig. Denne egenskab er simpelthen en henvisning tilprototypeaf den funktion, som objektet blev lavet fra. Det kan tilgås i scriptet ved hjælp af en speciel getter-setter (emne for en anden dag) kaldet __proto__. Der er andre nye måder at få adgang til denne prototype, men for kortfattethed vil jeg henvise til[[Prototype]]bruger __proto__.
var obj = {}var obj1 = new Object()

Ovennævnte to udsagn er lige udsagn, når de bruges til at skabe et nyt objekt, men der sker meget, når vi udfører nogen af ​​disse udsagn.

Når jeg laver et nyt objekt, er det tomt. Faktisk er det ikke tomt, fordi det er en forekomst afObjectkonstruktør, og det får iboende en reference til prototypeaf Object,som peges på af __proto__af det nyoprettede objekt.

Hvis vi ser på den prototypeaf Objectkonstruktøren funktion, det ser det samme som den __proto__af obj. i virkeligheden, er de to pejlemærker, der henviser til det samme objekt.

obj.__proto__ === Object.prototype//true

Hver prototypeaf en funktionhar en iboende egenskab kaldet constructorsom er en markør til selve funktionen. I tilfælde af Objectfunktion , den prototypeharconstructorsom peger tilbage til Object.

Object.prototype.constructor === Object//true

På billedet ovenfor er venstre side den udvidede visning af Objectkonstruktøren. Du må undre dig over, hvad der er alle disse andre funktioner over det. Funktioner er objekter , så de kan have egenskaber over dem, som andre objekter kan.

Hvis du ser nøje, har selve Object(til venstre) en__proto__hvilket betyder at Objectskal være lavet af en anden konstruktør, der har prototype. AsObjecter et funktionsobjekt, skal det være lavet ved hjælp af Functionkonstruktør.

__proto__af Objectser ud som prototypeden Function. Når jeg kontrollerer begge lighed, viser de sig at være de samme objekter.

Object.__proto__ === Function.prototype//true

Hvis du ser nøje, vil du se Functionselv har en __proto__hvilket betyder at Functionkonstruktørfunktionskal være lavet fra en eller anden konstruktorfunktion, der har en prototype. SomFunctioni sig selv er en funktion , den skal være lavet ved hjælp afFunctionkonstruktør, det vil sige sig selv. Jeg ved, det lyder underligt, men når du tjekker det, viser det sig at være sandt.

Det __proto__af Functionogprototypeaf Functionerfaktisk to henvisninger, der henviser til det samme objekt.

Function.prototype === Function.__proto__\\true

Som nævnt tidligere, den constructoraf enhverprototypeskal pege på den funktion, der ejer det prototype.Det constructoraf prototypeaf Functionpeger tilbage på Functionsig selv.

Function.prototype.constructor === Function\\true

Igen, den prototypeaf Functionhar en __proto__. Nå, det er ingen overraskelse ... prototypeer et objekt, det kan have en. Men bemærk også, at det peger påprototypeaf Object.

Function.prototype.__proto__ == Object.prototype\\true

Så vi kan have et masterkort her:

instanceof Operatora instanceof b

Detinstanceofoperatøren ser efter objektet bpeget på afnogen af de constructor(r) af lænket__proto__tændt a. Læs det igen! Hvis den finder en sådan reference, vender den tilbagetrueandet false.

Nu vender vi tilbage til vores første fire instanceofudsagn. Jeg har skrevet tilsvarende udsagninstanceofVend tilbage truefor følgende:

Object instanceof FunctionObject.__proto__.constructor === Function
Object instanceof ObjectObject.__proto__.__proto__.constructor === Object
Function instanceof FunctionFunction.__proto__.constructor === Function
Function instanceof ObjectFunction.__proto__.__proto__.constructor === Object

Pis !! Selv spaghetti er mindre sammenfiltret, men jeg håber, at tingene er klarere nu.

Her har jeg noget, som jeg ikke havde påpeget tidligere, at prototypeafObjecthar ikke en __proto__.

Faktisk har den en __proto__men det er lig med null. Kæden måtte ende et eller andet sted, og den ender her.

Object.prototype.__proto__\\null

Vores Object, Function,Object.prototype ogFunction.prototypehar også egenskaber, der er funktioner, såsom Object.assign,Object.prototype.hasOwnProperty ogFunction.prototype.call. Dette er interne funktioner, som ikke har prototypeog også er forekomster af Functionog har en__proto__som er en markør til Function.prototype.

Object.create.__proto__ === Function.prototype\\true

Du kan udforske andre konstruktørfunktioner som ArrayogDate, eller tag deres genstande og se efter prototypeog__proto__. Jeg er sikker på, at du vil være i stand til at finde ud af, hvordan alt er forbundet.

Ekstra forespørgsler:

Der er endnu et spørgsmål, der konsekvent mig for et stykke tid: Hvorfor er det, at prototypeaf Objecter genstand og prototypemed Functioner funktionen objekt ?

Herer en god forklaring på det, hvis du tænkte det samme.

Et andet spørgsmål, der indtil nu kan være et mysterium for dig, er: Hvordan får primitive datatyper funktioner som toString(),substr() og toFixed()?Dette forklares godt her .

Ved hjælp af prototypekan vi få arv til at fungere med vores brugerdefinerede objekter i JavaScript. Men det er et emne for en anden dag.

Tak for læsningen!