de Pranav Jindal
Prototip în JavaScript: este ciudat, dar iată cum funcționează
Următoarele patru linii sunt suficiente pentru a deruta majoritatea dezvoltatorilor JavaScript:
Object instanceof Function//true
Object instanceof Object//true
Function instanceof Object//true
Function instanceof Function//true
Prototipul în JavaScript este unul dintre cele mai uimitoare concepte, dar nu îl puteți evita. Indiferent cât de mult îl ignorați, veți întâlni puzzle-ul prototip în timpul vieții dvs. JavaScript.
Să recunoaștem deci.
Începând cu elementele de bază, există următoarele tipuri de date în JavaScript:
- nedefinit
- nul
- număr
- şir
- boolean
- obiect
Primele cinci sunt tipuri de date primitive. Acestea stochează o valoare de tipul lor, cum ar fi un boolean și pot fi adevărate sau false.
Ultimul „obiect” este un tip de referință pe care îl putem descrie ca o colecție de perechi cheie-valoare (dar este mult mai mult).
În JavaScript, obiecte noi sunt create folosind Constructor de obiecte funcţie (sau obiect literal {}
) care oferă metode generice precum toString()
și valueOf()
.
Funcțiile din JavaScript sunt obiecte speciale care pot fi „numit ”. Le realizăm și folosind Funcția constructor de funcții (sau funcțional literal). Faptul că acestea constructori sunt obiecte, precum și funcția m-a încurcat întotdeauna, în același mod în care ghicitoarea cu ouă de pui îi încurcă pe toți.
Înainte de a începe cu prototipuri, vreau să clarific că există două prototipuri în JavaScript:
-
prototip: Acesta este un obiect special care este atribuit ca proprietate a oricărei funcții pe care o realizați în JavaScript. Permiteți-mi să fiu clar aici, este deja prezent pentru orice funcție pe care o faceți, dar nu este obligatorie pentru funcțiile interne furnizate de JavaScript (și funcția returnată de
bind
). Acestprototype
este același obiect la care este indicat[[Prototype]]
(vezi mai jos) a obiectului nou creat din acea funcție (folosindnew
cuvânt cheie). -
[[Prototype]]: Aceasta este o proprietate ascunsă cumva pe fiecare obiect care este accesat de contextul de rulare dacă o anumită proprietate care este citită pe obiect nu este disponibilă. Această proprietate este pur și simplu o referință la
prototype
a funcției din care a fost făcut obiectul. Poate fi accesat în script folosind special getter-setter (subiect pentru altă zi) numit__proto__
. Există alte modalități noi de a accesa acest prototip, dar, din motive de scurtă durată, mă voi referi la[[Prototype]]
folosind__proto__
.
var obj = {}var obj1 = new Object()
Cele două afirmații de mai sus sunt afirmații egale atunci când sunt utilizate pentru a crea un obiect nou, dar multe se întâmplă atunci când executăm oricare dintre aceste afirmații.
Când fac un obiect nou, acesta este gol. De fapt nu este gol deoarece este o instanță a Object
constructor și obține în mod inerent o referință de prototype
de Object,
care este indicat de __proto__
a obiectului nou creat.
Dacă ne uităm la prototype
de Object
funcția constructor, arată la fel ca __proto__
de obj.
De fapt, acestea sunt două indicații care se referă la același obiect.
obj.__proto__ === Object.prototype//true
Fiecare prototype
a unei funcții are o proprietate inerentă numită constructor
care este un indicator către funcția în sine. În cazul în care Object
funcţie, prototype
are constructor
care arată înapoi la Object
.
Object.prototype.constructor === Object//true
În imaginea de mai sus, partea stângă este imaginea extinsă a Object
constructor. Trebuie să vă întrebați care sunt toate celelalte funcții ale acestuia. Ei bine, funcțiile sunt obiecte, astfel încât să poată avea proprietăți asupra lor ca și alte obiecte.
Dacă vă uitați atent, Object
(pe stânga) în sine are o __proto__
ceea ce înseamnă că Object
trebuie să fi fost realizat dintr-un alt constructor care are un prototype.
La fel de Object
este un obiect funcțional, trebuie să fi fost realizat folosind Function
constructor.
__proto__
de Object
arată la fel ca prototype
de Function
.Când verific egalitatea ambelor, ele se dovedesc a fi aceleași obiecte.
Object.__proto__ === Function.prototype//true
Dacă vă uitați atent, veți vedea Function
în sine are o __proto__
ceea ce înseamnă că Function
funcția constructor trebuie să fi fost făcută dintr-o funcție constructor care are o prototype
. La fel de Function
în sine este un funcţie, trebuie să fi fost realizat folosind Function
constructor, adică el însuși. Știu că sună ciudat, dar când îl verifici, se dovedește a fi adevărat.
__proto__
de Function
și prototype
de Function
sunt de fapt două indicii referitoare la același obiect.
Function.prototype === Function.__proto__\true
După cum sa menționat anterior, constructor
din oricare prototype
ar trebui să indice spre funcția care deține asta prototype.
constructor
de prototype
de Function
arată înapoi la Function
în sine.
Function.prototype.constructor === Function\true
Din nou, prototype
de Function
are o __proto__
Ei bine, nu este o surpriză … prototype
este un obiect, poate avea unul. Dar observați, de asemenea, că indică prototype
de Object
.
Function.prototype.__proto__ == Object.prototype\true
Deci, putem avea o hartă principală aici:
instanceof Operatora instanceof b
instanceof
operatorul caută obiectul b
arătat de oricare dintre constructor
(s) de înlănțuit __proto__
pe a
. Citește asta din nou! Dacă găsește o astfel de referință, se întoarce true
altceva false
.
Acum revenim la primele noastre patru instanceof
declarații. Am scris declarații corespunzătoare care fac instanceof
întoarcere true
pentru următoarele:
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
Phew !! Chiar și spaghetele sunt mai puțin încurcate, dar sper că lucrurile sunt mai clare acum.
Aici am ceva ce nu am subliniat mai devreme că prototype
de Object
nu are __proto__
.
De fapt are un __proto__
dar asta este egal cu null
. Lanțul trebuia să se termine undeva și se termină aici.
Object.prototype.__proto__\null
Al nostru Object
, Function
, Object.prototype
și Function.prototype
au, de asemenea, proprietăți care sunt funcții, cum ar fi Object.assign
, Object.prototype.hasOwnProperty
și Function.prototype.call
. Acestea sunt funcții interne care nu au prototype
și sunt, de asemenea, cazuri de Function
și au o __proto__
care este un indicator către Function.prototype
.
Object.create.__proto__ === Function.prototype\true
Puteți explora alte funcții de constructor cum ar fi Array
și Date
, sau luați obiectele lor și căutați prototype
și __proto__
. Sunt sigur că vei putea distinge modul în care totul este conectat.
Interogări suplimentare:
Mai există o întrebare care m-a bătut o vreme: De ce este asta prototype
de Object
este obiect și prototype
de Function
este obiect funcțional?
Aici este o explicație bună pentru asta dacă te-ai gândi la fel.
O altă întrebare care ar putea fi un mister pentru dumneavoastră până acum este: Cum primesc funcții tipurile de date primitive toString()
, substr()
și toFixed()
? Acest lucru este bine explicat Aici.
Folosind prototype
, putem face moștenirea să funcționeze cu obiectele noastre personalizate în JavaScript. Dar acesta este un subiect pentru o altă zi.
Mulțumesc pentru lectură!