Când definiți o funcție în cadrul JavaScript, acesta este livrat cu câteva proprietăți predefinite; unul dintre acestea este prototipul iluzoriu. În acest articol, voi detalia ce este și de ce ar trebui să îl utilizați în proiectele dvs..
Proprietatea prototip este inițial un obiect gol și poate avea membri adăugați la acesta - așa cum ați face orice alt obiect.
var myObject = funcția (nume) this.name = nume; returnați acest lucru; ; console.log (typeof myObject.prototype); // obiect myObject.prototype.getName = funcția () return this.name; ;
În fragmentul de mai sus, am creat o funcție, dar dacă sunăm myObject ()
, va returna pur și simplu fereastră
obiect, deoarece a fost definit în domeniul global. acest
va reveni, prin urmare, obiectul global, deoarece nu a fost încă instanțiat (mai multe despre acest lucru mai târziu).
console.log (fereastra myObject () ===); // Adevărat
Fiecare obiect din JavaScript are o proprietate "secretă".
Înainte de a continua, aș vrea să discutăm legătura "secretă" care face ca prototipul să funcționeze așa cum o face.
Fiecare obiect din JavaScript are o proprietate "secret" adăugată la acesta atunci când este definită sau instanțiată, denumită __proto__
; acesta este modul în care lanțul prototip este accesat. Cu toate acestea, nu este o idee bună să accesați __proto__
în aplicația dvs., deoarece nu este disponibilă în toate browserele.
__proto__
proprietatea nu trebuie confundată cu prototipul unui obiect, deoarece sunt două proprietăți separate; Acestea fiind spuse, ele merg mână în mână. Este important să faceți această distincție, deoarece poate fi destul de confuză la început! Ce înseamnă exact asta? Lasă-mă să explic. Când am creat myObject
, definim un obiect de tip Funcţie
.
console.log (typeof myObject); // funcția
Pentru cei care nu știu, Funcţie
este un obiect predefinit în JavaScript și, ca rezultat, are propriile proprietăți (de ex. lungime
și argumente
) și metode (de ex. apel
și aplica
). Și da, și ea are propriul obiect prototip, precum și secretul __proto__
legătură. Acest lucru înseamnă că, undeva în motorul JavaScript, există un pic de cod care ar putea fi similar cu următoarele:
Function.prototype = argumente: null, length: 0, call: function () // secret code, se aplica: function () // secret code
În realitate, probabil că nu ar fi prea simplist; acest lucru este doar pentru a ilustra modul în care lanțul prototip funcționează.
Așa că am definit myObject
ca o funcție și a dat un argument, Nume
; dar nu am stabilit nicio proprietate, cum ar fi lungime
sau metode, cum ar fi apel
. Deci, de ce funcționează următoarele?
console.log (myObject.length); // 1 (fiind suma argumentelor disponibile)
Aceasta este pentru că, atunci când am definit myObject
, a creat o __proto__
proprietate și a stabilit valoarea sa Function.prototype
(ilustrate în codul de mai sus). Deci, când accesăm myObject.length
, caută o proprietate a lui myObject
denumit lungime
și nu găsește unul; apoi călătorește în sus prin lanț __proto__ legătură
, găsește proprietatea și o returnează.
S-ar putea să te întrebi de ce lungime
este setat sa 1
si nu 0
- sau orice alt număr pentru acest fapt. Asta pentru ca myObject
este de fapt un exemplu de Funcţie
.
console.log (funcția myObject de exemplu); // true console.log (funcția myObject ===); // fals
Atunci când se creează o instanță a unui obiect, __proto__
proprietatea este actualizată pentru a indica prototipul constructorului, care, în acest caz, este Funcţie
.
console.log (myObject .__ proto__ === Function.prototype) // true
În plus, atunci când creați un nou Funcţie
obiect, codul nativ din interiorul Funcţie
constructorul va număra numărul de argumente și va actualiza this.length
în consecință, care, în acest caz, este 1
.
Dacă, totuși, vom crea un nou exemplu de myObject
folosind nou
cuvinte cheie, __proto__
va indica myObject.prototype
la fel de myObject
este constructorul noului nostru exemplu.
var myInstance = noul myObject ("foo"); console.log (myInstance .__ proto__ === myObject.prototype); // Adevărat
În plus față de accesul la metodele native din cadrul Funcţie
.prototip, cum ar fi apel
și aplica
, acum avem acces la myObject
metodă, getName
.
console.log (myInstance.getName ()); // foo var mySecondInstance = noul myObject ("bar"); console.log (mySecondInstance.getName ()); / / bar console.log (myInstance.getName ()); // foo
După cum vă puteți imagina, acest lucru este destul de util, deoarece poate fi folosit pentru a schița un obiect și pentru a crea cât mai multe situații, după cum este necesar - ceea ce mă conduce la subiectul următor!
Spuneți, de exemplu, că dezvoltăm un joc de pânză și că avem nevoie de mai multe (posibil sute de) obiecte pe ecran simultan. Fiecare obiect are proprietăți proprii, cum ar fi X
și y
coordonatele, lăţime
,înălţime
, și multe altele.
Am putea face acest lucru după cum urmează:
var Mio.floor ((Math.random () * myCanvasHeight) + 1), lățimea: 10, înălțimea: 10, trageți: function () myCanvasContext.fillRect (this.x, this.y, this.width, this.height); ...; var () (* Math.from) (* Math.random () * myCanvasWidth) + 1), y: Math.floor ((Math.random trageți: function () myCanvasContext.fillRect (this.x, this.y, this.width, this.height); ...;
... face asta de 98 de ori mai mult ...
Ceea ce va face este să creați toate aceste obiecte în memorie - toate cu definiții separate pentru metode, cum ar fi a desena
și orice alte metode pot fi necesare. Acest lucru nu este cu siguranță ideal, deoarece jocul va umfla browserele alocate java de memorie, și va face să ruleze foarte lent ... sau chiar să nu mai răspundă.
În timp ce acest lucru probabil că nu se va întâmpla doar cu 100 de obiecte, acesta poate servi drept o lovitură de performanță, deoarece va trebui să caute o sută de obiecte diferite, prototip
obiect.
Pentru a face ca aplicația să ruleze mai repede (și să urmeze cele mai bune practici), putem (re) defini proprietatea prototip a GameObject
; fiecare caz de GameObject
va face referire la metodele din interiorul acestuia GameObject.prototype
ca și cum ar fi propriile metode.
// definește funcția constructorului GameObject var GameObject = funcție (lățime, înălțime) this.x = Math.floor ((Math.random () * myCanvasWidth) + 1); this.y = Math.floor ((Math.random () * myCanvasHeight) + 1); this.width = width; this.height = înălțime; returnați acest lucru; ; // (re) definește obiectul prototip GameObject GameObject.prototype = x: 0, y: 0, lățimea: 5, lățimea: 5, desena: function () myCanvasContext.fillRect (this.x, this.y .width, this.high); ;
Putem apoi instantiza GameObject de 100 de ori.
var x = 100, arrayOfGameObjects = []; nu arrayOfGameObjects.push (noul GameObject (10, 10)); în timp ce (x--);
Acum avem o serie de 100 GameObjects, care au același prototip și aceeași definiție a desena
care economiseste drastic memorie in cadrul aplicatiei.
Când sunăm a desena
metoda, se va referi la aceeași funcție.
var GameLoop = funcție () pentru (gameObject în arrayOfGameObjects) gameObject.draw (); ;
Prototipul unui obiect este un obiect viu, ca să spunem așa. Acest lucru inseamna pur si simplu ca, daca, dupa ce creem toate instantele GameObject, decidem ca, in loc sa desenezi un dreptunghi, vrem sa trasam un cerc, sa ne putem actualiza GameObject.prototype.draw
în consecință.
GameObject.prototype.draw = funcția () myCanvasContext.arc (acest.x, acest.y, acest.width, 0, Math.PI * 2, true);
Și acum, toate cazurile anterioare de GameObject
și orice instanțe viitoare vor desena un cerc.
Da, acest lucru este posibil. Este posibil să fiți familiarizați cu bibliotecile JavaScript, cum ar fi Prototype, care profită de această metodă.
Să folosim un exemplu simplu:
String.prototype.trim = funcția () return this.replace (/ ^ \ s + | \ s + $ / g, ");;
Acum putem accesa aceasta ca o metodă a oricărui șir:
"Foo bar" .trim (); // "foo bar"
Există totuși un mic dezavantaj în acest sens. De exemplu, puteți utiliza acest lucru în aplicația dvs.; dar un an sau doi în jos pe drum, un browser poate implementa o versiune actualizată a JavaScript care include un nativ tunde
metoda în cadrul Şir
e prototipul. Aceasta înseamnă că definiția ta tunde
va suprascrie versiunea nativă! Hopa! Pentru a depăși acest lucru, putem adăuga o simplă verificare înainte de a defini funcția.
dacă (String.prototype.trim) String.prototype.trim = funcția () return this.replace (/ ^ \ s + | \ s + $ / g,);
Acum, dacă există, va folosi versiunea nativă a tunde
metodă.
Ca regulă generală, este în general considerată o bună practică pentru a evita extinderea obiectelor native. Dar, ca în orice caz, regulile pot fi rupte, dacă este necesar.
Sperăm că acest articol a aruncat o lumină asupra coloanei vertebrale a prototipului JavaScript. Ar trebui să vă deplasați acum la crearea de aplicații mai eficiente.
Dacă aveți întrebări cu privire la prototip, spuneți-mi comentariile și voi face tot posibilul pentru a le răspunde.