dacă sunteți nou în JavaScript, este posibil să vă confruntați deja cu unele dintre comportamentele amuzante pe care le oferă această limbă (expoziția A). La început, aceste ciudățenii ciudate ar putea părea ridicole și frustrante, dar promit că există o metodă în spatele acestei nebunii.
unul dintre cele mai grele obstacole de depășit, în umila mea părere, este diferența dintre trecerea prin valoare și trecerea prin referință. De ce este acest concept atât de complicat? Pentru început, cu siguranță puteți ajunge destul de departe fără a înțelege cu adevărat modul în care JavaScript interacționează cu valorile primitive și valorile de referință. Acest lucru poate, și cel mai adesea, va duce la o tona de bug-uri, care sunt greu pentru a urmări în jos și enervant pentru a repara. În al doilea rând, acesta este un concept care va apărea în interviurile tehnice, astfel încât neînțelegerea acestuia va conta ca un steag roșu imens împotriva ta.
nu te teme, prietene cititor! Să înceapă educația …
tipuri de date Primitive
în JavaScript, putem împărți tipurile de date în două găleți diferite, tipuri de date primitive și obiecte.
există șase tipuri de date primitive în JavaScript: string
, number
, boolean
, undefined
, null
, și symbol
ca de ES6.
tipurile de date Primitive sunt transmise sau copiate după valoare și sunt imuabile, ceea ce înseamnă că valoarea existentă nu poate fi modificată așa cum o matrice sau un obiect poate. Să aruncăm o privire la codul de mai jos pentru a vedea acest lucru în acțiune.
aici am creat două variabile, x = 10
și y = x
. Deoarece 10
este un număr și o valoare primitivă, atunci când setăm y = x
copiem de fapt valoarea, adică 10
și o atribuim y
. De asemenea, putem vizualiza acest lucru folosind graficul de mai jos.
dacă ar fi să schimbăm valoarea x
, am vedea că y
își păstrează valoarea 10
. Din nou, acest lucru se datorează faptului că valorile primitive sunt copiate, deci valoarea y
este independentă de valoarea x
. Gândiți-vă la asta ca la o fotocopie a unei imagini. După efectuarea copiei, aveți două imagini identice: un original și un facsimil. Dacă ar fi să tăiați originalul în jumătate, numai originalul ar fi modificat și facsimilul ar rămâne exact același.
obiecte de referință
obiectele, pe de altă parte, sunt transmise prin referință și indică o locație din memorie pentru valoare, nu valoarea în sine. Să aruncăm o privire la acest lucru în codul nostru.
în acest exemplu, x
este acum un obiect care indică {dog: "poodle"}
. Când creăm variabila y
și îi atribuim valoarea x
, acum putem accesa diferitele proprietăți ale x
care include valoarea pentru dog
, adică "poodle"
. Aceasta pare a fi exact aceeași logică folosită pentru valorile primitive, dar să aruncăm o privire la graficul nostru la îndemână de mai jos pentru a vedea diferența subtilă, dar importantă.
acum, această diagramă arată puțin diferită de atunci când variabilele noastre x
și y
dețineau tipuri de date primitive. În această versiune, vedem că valorile pentru ambele x
și y
nu sunt tipuri de date, ci referințe la o adresă din memorie, aceeași adresă de fapt! Acum să aruncăm o privire la ce se întâmplă cu x
dacă adăugăm o nouă proprietate a size
la y
…
x
returnează încă un obiect, dar acum are o proprietate suplimentară de size
de asemenea! Din nou, acest lucru se datorează faptului că atât x
, cât și y
indică același obiect de referință, astfel încât orice modificări aduse unei variabile vor fi vizibile în cealaltă.
pentru a mă ajuta să-mi amintesc acest concept, îmi place să mă gândesc la valorile de referință ca la o casă și la variabile ca la oameni care locuiesc în acea casă. Toți locuitorii (variabilele) pot spune „Am o casă” și indică aceeași casă. Dacă un singur rezident decide că vrea să picteze casa în galben, atunci toți locuitorii au acum o casă galbenă, deoarece este împărțită.
să aruncăm o privire la încă un exemplu care conține o varietate de obiecte de referință.
în acest cod, începem cu o variabilă person
care conține proprietăți De name
, age
și hobbies
. Când imprimăm acest obiect pe consolă, obținem exact ceea ce ne așteptăm — același obiect pe care tocmai l-am creat.
apoi, avem o funcție numită changePerson
care preia un argument, face câteva modificări, apoi returnează un obiect. Când creăm variabila thirdPerson
, invocăm funcția changePerson
trecând Obiectul nostru original de person
în ea. Partea interesantă este ce se întâmplă când imprimăm din nou pe consola thirdPerson
și person
.
observați că console.log(thirdPerson)
returnează un obiect cu totul nou cu proprietăți noi. Acum uita-te la ceea ce console.log(person)
se întoarce. Acest lucru este similar cu obiectul nostru original, dar conține noi valori de proprietate care au fost introduse în funcția noastră changePerson
.
verificarea egalității
în cele din urmă, să aruncăm o privire la modul în care tipurile de date primitive și obiectele de referință se comportă cu operatorii de egalitate.
când vine vorba de tipuri de date primitive, nu contează ce este în dreapta semnului =
, atâta timp cât valorile sunt aceleași. Putem vedea acest lucru mai sus cu variabile a
și b
care sunt scrise diferit, dar se evaluează la aceeași valoare atunci când folosim ===
, operatorul de egalitate strictă.
opusul este adevărat în al doilea exemplu pentru dog
și cat
. Deși poate părea că acestea conțin valori identice, este o matrice și un obiect de referință, ceea ce înseamnă că
===
verifică dacă ambele dog
și cat
au aceeași referință la valoarea din memorie. Pe de altă parte, bird === dog
este adevărat deoarece împărtășesc același obiect de referință.
concluzie
și care încheie introducerea noastră în pass by value vs pass by reference. Există mai multe subiecte care pot fi acoperite sub această umbrelă fundamental, inclusiv ce se întâmplă atunci când o referință este suprascrisă sau pierdută, cum să copiați o referință pentru a crea un obiect nou și asigurați-vă că copia este o copie profundă, doar pentru a numi câteva. Aș recomanda verificarea resurselor pe care le-am folosit mai jos, care intră în unele dintre aceste subiecte suplimentare în detaliu.