Mai mult
Cum fac corect clona un obiect JavaScript?
Am un obiect, "x". Am'd place să-l copieze ca obiect "y", astfel că modificările la " y "nu modifica "x". Mi-am dat seama că copierea obiectelor derivate din built-in JavaScript obiectele vor rezulta în plus, proprietăți nedorite. Asta e't o problemă, de când am'm copierea unul de-al meu, literal-obiecte construite.
Cum fac corect clona un obiect JavaScript?
2906
64
Pentru a face acest lucru pentru orice obiect în JavaScript nu va fi simplă sau directă. Va rula în problema în mod eronat ridica atributele de obiect's prototip care ar trebui să fie lăsat în prototip și nu copiate la noi de exemplu. Dacă, de exemplu, adăugați o "clonă" metoda de a
Obiect.prototip
, ca niște răspunsuri descriu, va trebui să în mod explicit sări peste acest atribut. Dar dacă există și alte metode suplimentare adăugate la Obiect.prototip, sau alte intermediar prototipuri, pe care nu't știu despre? În acest caz, veți copia atributele nu ar trebui't, astfel încât aveți nevoie pentru a detecta neprevăzute, non-locale atribute cu [
hasOwnProperty`]1 metoda.În plus față de non-nenumărate atribute, ai'll întâlni o mai greu problemă atunci când încercați să copiați obiecte care au proprietăți ascunse. De exemplu, "prototip" este o ascunse proprietate a unei funcții. De asemenea, un obiect's prototip este referit cu atributul
__proto__
, care este, de asemenea, ascunse, și nu va fi copiată de către un pentru/în buclă iterarea peste obiectul sursă's atribute. Cred că__proto__
ar putea fi specifice pentru Firefox's JavaScript interpret și ar putea fi ceva diferit în alte browsere, dar veți obține imaginea. Nu totul este enumerable. Puteți copia un atribut ascuns, dacă știi numele, dar eu nu't știu de orice mod de a descoperi în mod automat.Încă un obstacol în căutarea pentru o soluție elegantă este problema înființării prototipul moștenire corect. Dacă sursa ta de obiect's prototip este "Obiect", apoi pur și simplu a crea un nou obiect general cu
{}
va funcționa, dar dacă sursa's prototip este un descendent al "Obiect", atunci aveți de gând să fi dispărut suplimentare membrii din acel prototip care ai lipsit de ajutorulhasOwnProperty
filtru, sau care au fost în prototip, dar nu't enumerable în primul rând. O soluție ar putea fi să suni la sursă, obiect'sconstructor de proprietate pentru a obține copia inițială obiect și apoi copiați atributele, dar atunci tot nu va primi non-nenumărate atribute. De exemplu, o [
Data`]2 obiect stochează datele sale ca o ascunse membru:La data de coarde pentru " d1 "va fi de 5 secunde în spatele lui "d2". O modalitate de a face o "Data" la fel ca altul este de asteptare
setTime
metoda, dar care este specific pentru "Data" de clasă. Eu nu't cred că există un anti-glont general soluție la această problemă, deși aș fi fericit să fi greșit!Când a trebuit să pună în aplicare general adânc copierea am ajuns compromite presupunând că aș avea nevoie de doar pentru a copia un simplu "Obiect", "Matrice", "Data", "String", "Număr", sau "Boolean". Ultimele 3 tipuri sunt imuabile, așa că am putea face o copie superficială și nu vă faceți griji cu privire la aceasta schimbare. Am mai presupus că orice elemente conținute în "Obiect" sau "Matrice" ar fi, de asemenea, una dintre cele 6 tipuri simple în această listă. Acest lucru poate fi realizat cu cod, cum ar fi următoarele:
Funcția de mai sus va funcționa în mod adecvat pentru 6 tipuri simple am menționat, atâta timp cât datele în obiecte și tablouri de a forma o structură de arbore. Asta este, nu e't mai mult de o referință la aceleași date în obiect. De exemplu:
Acesta nu va fi capabil să se ocupe de orice obiect JavaScript, dar poate fi suficient pentru multe scopuri, atâta timp cât nu't se presupună că aceasta va funcționa doar pentru orice ai arunca la ea.
Dacă nu utilizați
Data, funcții, nedefinit, sau Infinit în obiect, una foarte simplă linie este
JSON.analiza(JSON.stringify(obiect))`:Aceasta funcționează pentru toate tipurile de obiecte care conțin obiecte, tablouri, siruri de caractere, boolean și numere.
A se vedea, de asemenea, acest articol despre structurat clona algoritm de browsere care este folosit la transmiterea de mesaje către și de la un lucrător. Acesta conține, de asemenea, o funcție de adâncime de clonare.
Cu jQuery, puteți copie superficială cu prelungi:
modificări ulterioare ale
copiedObject
nu va afectaoriginalObject
, și vice-versa.Sau pentru a face o copie profundă:
În ECMAScript 6 există Obiect.assign metoda, care copiază valorile tuturor enumerable propriile proprietăți de la un obiect la altul. De exemplu:
Dar să fie conștienți de faptul că, obiecte imbricate sunt încă copiate ca referință.
Pe MDN:
Obiect.atribui({}, a)
JSON.analiza(JSON.stringify(a))
Nu este nevoie de biblioteci externe, dar ai nevoie pentru a verifica compatibilitate browser-ul prima.
Există multe răspunsuri, dar nici unul care menționează Obiect.crea de ECMAScript 5, care, desigur, nu-ți dau o copie exactă, dar stabilește sursa ca prototip al noului obiect.
Astfel, acest lucru nu este un răspuns exact la întrebare, dar este de o singură linie de soluție și, astfel, elegant. Și aceasta funcționează cel mai bine pentru 2 cazuri:
Exemplu:
De ce nu am lua în considerare această soluție să fie superior? L's nativi, astfel, nu looping, nu recursivitate. Cu toate acestea, browsere mai vechi vor avea nevoie de un polyfill.
Un mod elegant de a clona un obiect Javascript într-o singură linie de cod
Un `Obiect.atribuie metodă este parte a ECMAScript 2015 (ES6) standard și face exact ceea ce ai nevoie.
Citeste more...
La polyfill pentru susținerea browsere mai vechi:
Există mai multe probleme cu cele mai multe soluții pe internet. Așa că am decis să fac un follow-up, care include, de ce a acceptat răspunsul ar trebui't fi acceptat.
pornind de situație
Vreau sa profund-copie un Javascript "Obiect" cu toți copiii ei și copiii lor și așa mai departe. Dar de când am'm nu fel de normal producător meu "Obiect" are normal
properties
,structuri circulare "și chiar" obiecte imbricate
.Deci sa's a crea o structură circulară "și un" obiect imbricate prima.
Las's aduce totul împreună într-un "Obiect" nume "a".
Apoi, vrem să copie " a "într-o variabilă numită" b " și să-l transforme.
Știi ce s-a întâmplat aici, pentru că dacă nu te-ar't chiar terenul pe această mare întrebare.
Acum las's a găsi o soluție.
JSON
Prima încercare am încercat a fost folosind
JSON
.Don't pierde prea mult timp pe el,'ll
TypeError: Convertirea structura circulară a JSON
.Recursiv copie (acceptat "răspuns")
Las's au o privire la răspunsul acceptat.
Arata bine, heh? L's o recursive copie a obiectului și se ocupă de alte tipuri, cum ar fi "Data", dar asta nu't o cerință.
Recursivitate și structuri circulare
nu't lucra bine împreună...
RangeError: Maxim call stack size depășit`soluție nativ
După cearta cu colegul meu, seful meu ne-a întrebat ce s-a întâmplat, și-a găsit o simplă soluție ** după unele googling. L's a numit
Obiect.crea
.Această soluție a fost adăugat la Javascript ceva timp în urmă și chiar se ocupă de
structură circulară
.și vezi, nu't de lucru cu imbricate în interiorul structurii.
polyfill pentru soluție nativ
Nu's un polyfill pentru Obiect.crea` în browser-ul vechi, doar ca IE 8. L's ceva de genul recomandate de Mozilla, și, desigur, l's nu este perfect și rezultatele în aceeași problemă ca soluție nativ.
Am'am pus " F " în afara domeniului de aplicare astfel încât să putem avea o privire la ceea ce
instanceof
ne spune.Aceeasi problema ca soluție nativ, dar un pic mai rău de ieșire.
cel mai bine (dar nu perfect) soluție
Atunci când săpat în jurul valorii de, am găsit o întrebare similară (https://stackoverflow.com/questions/10728412/in-javascript-when-performing-a-deep-copy-how-do-i-avoid-a-cycle-due-to-a-pro) pentru asta, dar cu o modalitate mai bună soluție.
Daca're bine cu o copie superficială, la underscore.js biblioteca are un clone metoda.
sau puteți să-l extindă ca
OK imaginați-vă că au acest obiect de mai jos și doriți să clona:
sau
răspunsul este în principal depeneds pe care ECMAscript de tine, folosind, în
ES6+
, puteți folosi pur și simplu Obiectul.atribui sa faci clona:sau folosind răspândit operator de genul asta:
Dar dacă folosești
ES5
, puteți folosi câteva metode, darJSON.stringify
, doar asigurați-vă că nu utilizați pentru o mare parte din date pentru a copia, dar ar putea fi o linie modalitate la îndemână în multe cazuri, ceva de genul asta:Unul deosebit de elegant soluție este de a utiliza JSON de codificare pentru a face profundă exemplare de obiecte care nu trebuie membru metode. Metodologia este de a JSON codifica obiect țintă, apoi de decodare, veți obține o copie sunteți în căutarea pentru. Tu poate decoda ori de câte ori doriți să faceți cât mai multe copii ca ai nevoie.
Desigur, funcțiile nu aparțin în JSON, astfel încât aceasta funcționează numai pentru obiecte fără membru metode.
Această metodologie a fost perfect pentru caz de utilizare, de când am'm depozitarea JSON blobs într-un magazin cheie-valoare, și atunci când acestea sunt expuse și obiecte în JavaScript API, fiecare obiect conține de fapt o copie a stării inițiale a obiectului astfel încât să putem calcula delta după ce apelantul a suferit mutații expuse obiect.
Puteți utiliza pur și simplu un răspândirea de proprietate pentru a copia un obiect fără referințe. Dar fii atent (vezi comentarii), ca 'copia' este doar pe cel mai mic obiect/matrice nivel. Imbricate proprietăți sunt încă referințe!
Complet clona:
Clona cu trimiteri la al doilea nivel:
JavaScript de fapt nu are suport adânc clone nativ. Utilizați o funcție de utilitate. De exemplu Ramda:
Pentru cei care folosesc AngularJS, există, de asemenea, metoda directă pentru clonare sau extinderea obiectelor din această bibliotecă.
sau
Mai mult, în unghiulare.copia documentația...
A. Levy's răspunsul este aproape completă, aici e mica mea contribuție: există o modalitate de cum să se ocupe recursiv bibliografie, vezi linia asta
dacă(acest lucru[v]==acest lucru) copia[attr] = copie;
Dacă obiectul este XML DOM element, trebuie să utilizați cloneNode în loc
dacă(acest lucru.cloneNode) se întoarcă acest lucru.cloneNode(true);
Inspirat de A. Levy's studiu exhaustiv și Calvin's prototipuri abordare, am oferit această soluție:
A se vedea, de asemenea, Andy Burke's notă în răspunsuri.
De la acest articol: Cum să copiați tablouri și obiecte în Javascript de Brian Huisman:
Aici este o funcție puteți folosi.
În ES-6 puteți folosi pur și simplu Obiect.atribui(...). Ex:
Un bun punct de referință este aici: https://googlechrome.github.io/samples/object-assign-es6/
În ECMAScript 2018
Fi conștienți de faptul că obiecte imbricate sunt încă copiat ca o referință.
Nou răspuns la o întrebare veche! Dacă aveți plăcerea de a avea folosind ECMAScript 2016 (ES6) cu a Răspândit Sintaxa, l's ușor.
Acest lucru oferă o metoda curat pentru o copie superficială a unui obiect. A face o copie profundă, în sensul makign o copie nouă de fiecare valoare în fiecare recursiv obiecte imbricate, necesită pe de grele soluții de mai sus.
JavaScript continuă evoluție.
Poti clona un obiect și de a elimina orice referire la precedenta, folosind o singură linie de cod. Pur și simplu:
Pentru browsere / motoare care nu acceptă în prezent Obiect.creați puteți utiliza această polyfill: