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?
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 ajutorul hasOwnProperty
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's constructor 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:
function clone(obj) {
if (null == obj || "object" != typeof obj) return obj;
var copy = obj.constructor();
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
}
return copy;
}
var d1 = new Date();
/* Executes function after 5 seconds. */
setTimeout(function(){
var d2 = clone(d1);
alert("d1 = " + d1.toString() + "\nd2 = " + d2.toString());
}, 5000);
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:
function clone(obj) {
var copy;
// Handle the 3 simple types, and null or undefined
if (null == obj || "object" != typeof obj) return obj;
// Handle Date
if (obj instanceof Date) {
copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
// Handle Array
if (obj instanceof Array) {
copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
copy[i] = clone(obj[i]);
}
return copy;
}
// Handle Object
if (obj instanceof Object) {
copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
}
return copy;
}
throw new Error("Unable to copy obj! Its type isn't supported.");
}
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:
// This would be cloneable:
var tree = {
"left" : { "left" : null, "right" : null, "data" : 3 },
"right" : null,
"data" : 8
};
// This would kind-of work, but you would get 2 copies of the
// inner node instead of 2 references to the same copy
var directedAcylicGraph = {
"left" : { "left" : null, "right" : null, "data" : 3 },
"data" : 8
};
directedAcyclicGraph["right"] = directedAcyclicGraph["left"];
// Cloning this would cause a stack overflow due to infinite recursion:
var cyclicGraph = {
"left" : { "left" : null, "right" : null, "data" : 3 },
"data" : 8
};
cyclicGraph["right"] = cyclicGraph;
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))`:
const a = {
string: 'string',
number: 123,
bool: false,
nul: null,
date: new Date(), // stringified
undef: undefined, // lost
inf: Infinity, // forced to 'null'
}
console.log(a);
console.log(typeof a.date); // Date object
const clone = JSON.parse(JSON.stringify(a));
console.log(clone);
console.log(typeof clone.date); // result of .toISOString()
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:
var copiedObject = jQuery.extend({}, originalObject)
modificări ulterioare ale copiedObject
nu va afecta originalObject
, și vice-versa.
Sau pentru a face o copie profundă:
var copiedObject = jQuery.extend(true, {}, originalObject)
În ECMAScript 6 există Obiect.assign metoda, care copiază valorile tuturor enumerable propriile proprietăți de la un obiect la altul. De exemplu:
var x = {myProp: "value"};
var y = Object.assign({}, x);
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:
var foo = { a : 1 };
var bar = Object.create(foo);
foo.a; // 1
bar.a; // 1
foo.a = 2;
bar.a; // 2 - prototype changed
bar.a = 3;
foo.a; // Still 2, since setting bar.a makes it an "own" property
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 `Obiect.atribuie metodă este parte a ECMAScript 2015 (ES6) standard și face exact ceea ce ai nevoie.
var clone = Object.assign({}, obj);
Obiect.assign() metodă este folosită pentru a copia valorile din toate nenumărate proprietăți proprii din unul sau mai multe obiecte de sursa de la un obiect țintă.
La polyfill pentru susținerea browsere mai vechi:
if (!Object.assign) {
Object.defineProperty(Object, 'assign', {
enumerable: false,
configurable: true,
writable: true,
value: function(target) {
'use strict';
if (target === undefined || target === null) {
throw new TypeError('Cannot convert first argument to object');
}
var to = Object(target);
for (var i = 1; i < arguments.length; i++) {
var nextSource = arguments[i];
if (nextSource === undefined || nextSource === null) {
continue;
}
nextSource = Object(nextSource);
var keysArray = Object.keys(nextSource);
for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
var nextKey = keysArray[nextIndex];
var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
if (desc !== undefined && desc.enumerable) {
to[nextKey] = nextSource[nextKey];
}
}
}
return to;
}
});
}
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.
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.
function Circ() {
this.me = this;
}
function Nested(y) {
this.y = y;
}
Las's aduce totul împreună într-un "Obiect" nume "a".
var a = {
x: 'a',
circ: new Circ(),
nested: new Nested('a')
};
Apoi, vrem să copie " a "într-o variabilă numită" b " și să-l transforme.
var b = a;
b.x = 'b';
b.nested.y = 'b';
Știi ce s-a întâmplat aici, pentru că dacă nu te-ar't chiar terenul pe această mare întrebare.
console.log(a, b);
a --> Object {
x: "b",
circ: Circ {
me: Circ { ... }
},
nested: Nested {
y: "b"
}
}
b --> Object {
x: "b",
circ: Circ {
me: Circ { ... }
},
nested: Nested {
y: "b"
}
}
Acum las's a găsi o soluție.
Prima încercare am încercat a fost folosind JSON
.
var b = JSON.parse( JSON.stringify( a ) );
b.x = 'b';
b.nested.y = 'b';
Don't pierde prea mult timp pe el,'ll TypeError: Convertirea structura circulară a JSON
.
Las's au o privire la răspunsul acceptat.
function cloneSO(obj) {
// Handle the 3 simple types, and null or undefined
if (null == obj || "object" != typeof obj) return obj;
// Handle Date
if (obj instanceof Date) {
var copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
// Handle Array
if (obj instanceof Array) {
var copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
copy[i] = cloneSO(obj[i]);
}
return copy;
}
// Handle Object
if (obj instanceof Object) {
var copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = cloneSO(obj[attr]);
}
return copy;
}
throw new Error("Unable to copy obj! Its type isn't supported.");
}
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ță.
var b = cloneSO(a);
b.x = 'b';
b.nested.y = 'b';
Recursivitate și structuri circularenu't lucra bine împreună...
RangeError: Maxim call stack size depășit`
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
.
var b = Object.create(a);
b.x = 'b';
b.nested.y = 'b';
Această soluție a fost adăugat la Javascript ceva timp în urmă și chiar se ocupă de structură circulară
.
console.log(a, b);
a --> Object {
x: "a",
circ: Circ {
me: Circ { ... }
},
nested: Nested {
y: "b"
}
}
b --> Object {
x: "b",
circ: Circ {
me: Circ { ... }
},
nested: Nested {
y: "b"
}
}
și vezi, nu't de lucru cu imbricate în interiorul structurii.
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.
function F() {};
function clonePF(o) {
F.prototype = o;
return new F();
}
var b = clonePF(a);
b.x = 'b';
b.nested.y = 'b';
Am'am pus " F " în afara domeniului de aplicare astfel încât să putem avea o privire la ceea ce instanceof
ne spune.
console.log(a, b);
a --> Object {
x: "a",
circ: Circ {
me: Circ { ... }
},
nested: Nested {
y: "b"
}
}
b --> F {
x: "b",
circ: Circ {
me: Circ { ... }
},
nested: Nested {
y: "b"
}
}
console.log(typeof a, typeof b);
a --> object
b --> object
console.log(a instanceof Object, b instanceof Object);
a --> true
b --> true
console.log(a instanceof F, b instanceof F);
a --> false
b --> true
Aceeasi problema ca soluție nativ, dar un pic mai rău de ieșire.
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.
function cloneDR(o) {
const gdcc = "__getDeepCircularCopy__";
if (o !== Object(o)) {
return o; // primitive value
}
var set = gdcc in o,
cache = o[gdcc],
result;
if (set && typeof cache == "function") {
return cache();
}
// else
o[gdcc] = function() { return result; }; // overwrite
if (o instanceof Array) {
result = [];
for (var i=0; i<o.length; i++) {
result[i] = cloneDR(o[i]);
}
} else {
result = {};
for (var prop in o)
if (prop != gdcc)
result[prop] = cloneDR(o[prop]);
else if (set)
result[prop] = cloneDR(cache);
}
if (set) {
o[gdcc] = cache; // reset
} else {
delete o[gdcc]; // unset again
}
return result;
}
var b = cloneDR(a);
b.x = 'b';
b.nested.y = 'b';
Și să's au o privire la ieșirea...
console.log(a, b);
a --> Object {
x: "a",
circ: Object {
me: Object { ... }
},
nested: Object {
y: "a"
}
}
b --> Object {
x: "b",
circ: Object {
me: Object { ... }
},
nested: Object {
y: "b"
}
}
console.log(typeof a, typeof b);
a --> object
b --> object
console.log(a instanceof Object, b instanceof Object);
a --> true
b --> true
console.log(a instanceof F, b instanceof F);
a --> false
b --> false
Cerințele sunt potrivite, dar există încă unele mici probleme, inclusiv schimbarea exemplu " de "imbricate" și "circ" la " Obiect`.
structura de copaci care împărtășesc o frunză câștigat't fi copiate, ele vor deveni independente frunze:
[Object] [Object]
/ \ / \
/ \ / \
|/_ _\| |/_ _\|
[Object] [Object] ===> [Object] [Object]
\ / | |
\ / | |
_\| |/_ \|/ \|/
[Object] [Object] [Object]
Ultima soluție folosind recursivitate și o memorie cache, nu poate fi cel mai bun, dar's a real profund-copie a obiectului. Acesta se ocupă de simplu properties
, `structuri circulare " și " imbricate obiect, dar acesta se va opri instanță de ei în timp ce clonarea.
[jsfiddle](
)OK imaginați-vă că au acest obiect de mai jos și doriți să clona:
let obj = {a:1, b:2, c:3}; //ES6
sau
var obj = {a:1, b:2, c:3}; //ES5
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:
let cloned = Object.assign({}, obj); //new {a:1, b:2, c:3};
sau folosind răspândit operator de genul asta:
let cloned = {...obj}; //new {a:1, b:2, c:3};
Dar dacă folosești ES5
, puteți folosi câteva metode, dar JSON.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:
let cloned = JSON.parse(JSON.stringify(obj));
//new {a:1, b:2, c:3};, can be handy, but avoid using on big chunk of data over and over
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.
var object1 = {key:"value"};
var object2 = object1;
object2 = JSON.stringify(object1);
object2 = JSON.parse(object2);
object2.key = "a change";
console.log(object1);// returns value
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:
let x = {a: 'value1'}
let x2 = {...x}
// => mutate without references:
x2.a = 'value2'
console.log(x.a) // => 'value1'
Clona cu trimiteri la al doilea nivel:
const y = {a: {b: 'value3'}}
const y2 = {...y}
// => nested object is still a references:
y2.a.b = 'value4'
console.log(y.a.b) // => 'value4'
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ă.
var destination = angular.copy(source);
sau
angular.copy(source, destination);
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:
Object.prototype.clone = function() {
if(this.cloneNode) return this.cloneNode(true);
var copy = this instanceof Array ? [] : {};
for(var attr in this) {
if(typeof this[attr] == "function" || this[attr]==null || !this[attr].clone)
copy[attr] = this[attr];
else if(this[attr]==this) copy[attr] = copy;
else copy[attr] = this[attr].clone();
}
return copy;
}
Date.prototype.clone = function() {
var copy = new Date();
copy.setTime(this.getTime());
return copy;
}
Number.prototype.clone =
Boolean.prototype.clone =
String.prototype.clone = function() {
return this;
}
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:
Object.prototype.clone = function() {
var newObj = (this instanceof Array) ? [] : {};
for (var i in this) {
if (i == 'clone') continue;
if (this[i] && typeof this[i] == "object") {
newObj[i] = this[i].clone();
} else newObj[i] = this[i]
} return newObj;
};
În ES-6 puteți folosi pur și simplu Obiect.atribui(...). Ex:
let obj = {person: 'Thor Odinson'};
let clone = Object.assign({}, obj);
Un bun punct de referință este aici: https://googlechrome.github.io/samples/object-assign-es6/
În ECMAScript 2018
let objClone = { ...obj };
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.
keepMeTheSame = {first: "Me!", second: "You!"};
cloned = {...keepMeTheSame}
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:
var obj1 = { text: 'moo1' };
var obj2 = Object.create(obj1); // Creates a new clone without references
obj2.text = 'moo2'; // Only updates obj2's text property
console.log(obj1, obj2); // Outputs: obj1: {text:'moo1'}, obj2: {text:'moo2'}
Pentru browsere / motoare care nu acceptă în prezent Obiect.creați puteți utiliza această polyfill:
// Polyfill Object.create if it does not exist
if (!Object.create) {
Object.create = function (o) {
var F = function () {};
F.prototype = o;
return new F();
};
}