Qual è il modo più efficace per clonare in profondità un object in JavaScript?

Qual è il modo più efficace per clonare un object JavaScript? Ho visto obj = eval(uneval(o)); in uso, ma non è standard e supportato solo da Firefox .

Ho fatto cose come obj = JSON.parse(JSON.stringify(o)); ma dubita dell’efficienza.

Ho anche visto funzioni di copia ricorsiva con vari difetti.
Sono sorpreso che non esista alcuna soluzione canonica.

Nota: questa è una risposta ad un’altra risposta, non una risposta adeguata a questa domanda. Se desideri avere una clonazione veloce dell’object, segui il consiglio di Corban nella risposta a questa domanda.


Voglio notare che il metodo .clone() in jQuery clona solo elementi DOM. Per clonare oggetti JavaScript, dovresti fare:

 // Shallow copy var newObject = jQuery.extend({}, oldObject); // Deep copy var newObject = jQuery.extend(true, {}, oldObject); 

Ulteriori informazioni possono essere trovate nella documentazione di jQuery .

Voglio anche notare che la copia profonda è in realtà molto più intelligente di quanto mostrato sopra: è in grado di evitare molte trappole (cercando di estendere in profondità un elemento DOM, ad esempio). È usato frequentemente nel core jQuery e nei plugin con grande effetto.

Leggi questo benchmark: http://jsben.ch/#/bWfk9

Nei miei precedenti test in cui la velocità era una preoccupazione principale che ho trovato

 JSON.parse(JSON.stringify(obj)) 

essere il modo più veloce per clonare in profondità un object (batte jQuery.extend con deep flag impostato su true del 10-20%).

jQuery.extend è piuttosto veloce quando il flag deep è impostato su false (clone poco profondo). È una buona opzione, perché include alcune logiche extra per la convalida del tipo e non copia su proprietà non definite, ecc., Ma anche questo rallenterà un po ‘.

Se si conosce la struttura degli oggetti che si sta tentando di clonare o si possono evitare array deep nested, è ansible scrivere un ciclo semplice for (var i in obj) per clonare l’object mentre si controlla hasOwnProperty e sarà molto più veloce di jQuery.

Infine, se si sta tentando di clonare una struttura di oggetti noti in un ciclo caldo, è ansible ottenere MOLTO MOLTO ALTRO PERFORMANCE semplicemente inserendo la procedura clone e costruendo manualmente l’object.

I motori di tracciamento JavaScript succhiano all’ottimizzazione for..in loop e controllando hasOwnProperty rallenteranno anche voi. Clone manuale quando la velocità è assoluta.

 var clonedObject = { knownProp: obj.knownProp, .. } 

JSON.parse(JSON.stringify(obj)) attenzione all’utilizzo del JSON.parse(JSON.stringify(obj)) sugli oggetti DateJSON.stringify(new Date()) restituisce una rappresentazione in formato stringa della data in formato ISO, che JSON.parse() non converte indietro a un object Date . Vedi questa risposta per maggiori dettagli .

Inoltre, tieni presente che, almeno in Chrome 65, la clonazione nativa non è la strada da percorrere. Secondo questo JSPerf , l’esecuzione della clonazione nativa con la creazione di una nuova funzione è quasi 800 volte più lenta rispetto all’utilizzo di JSON.stringify, che è incredibilmente veloce su tutta la linea.

Supponendo che tu abbia solo variabili e non funzioni nel tuo object, puoi semplicemente usare:

 var newObject = JSON.parse(JSON.stringify(oldObject)); 

Clonazione strutturata

HTML5 definisce un algoritmo di clonazione interno “strutturato” che può creare cloni profondi di oggetti. È ancora limitato a determinati tipi built-in, ma oltre ai pochi tipi supportati da JSON supporta anche Date, RegExps, Maps, Sets, Blob, FileLists, ImageDatas, array sparsi, matrici tipizzate e probabilmente più in futuro . Conserva anche i riferimenti all’interno dei dati clonati, permettendogli di supportare strutture cicliche e ricorsive che causerebbero errori per JSON.

Supporto diretto nei browser: in arrivo? 🙂

I browser non forniscono attualmente un’interfaccia diretta per l’algoritmo di clonazione strutturata, ma una funzione structuredClone() globaleClone structuredClone() viene triggersmente discussa in whatwg / html # 793 su GitHub e potrebbe essere in arrivo! Come attualmente proposto, utilizzarlo per la maggior parte degli scopi sarà semplice come:

 const clone = structuredClone(original); 

Fino a quando questo viene spedito, le implementazioni clone strutturate dei browser sono esposte solo indirettamente.

Soluzione asincrona: utilizzabile. 😕

Il modo più basso di creare un clone strutturato con le API esistenti è di pubblicare i dati attraverso una porta di un MessageChannels . L’altra porta emetterà un evento message con un clone strutturato del .data allegato. Sfortunatamente, ascoltare questi eventi è necessariamente asincrono e le alternative sincrone sono meno pratiche.

 class StructuredCloner { constructor() { this.pendingClones_ = new Map(); this.nextKey_ = 0; const channel = new MessageChannel(); this.inPort_ = channel.port1; this.outPort_ = channel.port2; this.outPort_.onmessage = ({data: {key, value}}) => { const resolve = this.pendingClones_.get(key); resolve(value); this.pendingClones_.delete(key); }; this.outPort_.start(); } cloneAsync(value) { return new Promise(resolve => { const key = this.nextKey_++; this.pendingClones_.set(key, resolve); this.inPort_.postMessage({key, value}); }); } } const structuredCloneAsync = window.structuredCloneAsync = StructuredCloner.prototype.cloneAsync.bind(new StructuredCloner); 

Esempio di utilizzo:

 const main = async () => { const original = { date: new Date(), number: Math.random() }; original.self = original; const clone = await structuredCloneAsync(original); // They're different objects: console.assert(original !== clone); console.assert(original.date !== clone.date); // They're cyclical: console.assert(original.self === original); console.assert(clone.self === clone); // They contain equivalent values: console.assert(original.number === clone.number); console.assert(Number(original.date) === Number(clone.date)); console.log("Assertions complete."); }; main(); 

Soluzioni alternative sincrone: terribile! 🤢

Non ci sono buone opzioni per creare cloni strutturati in modo sincrono. Ecco invece un paio di hack poco pratici.

history.pushState() e history.replaceState() creano entrambi un clone strutturato del loro primo argomento e assegnano quel valore a history.state . Puoi usarlo per creare un clone strutturato di qualsiasi object come questo:

 const structuredClone = obj => { const oldState = history.state; history.replaceState(obj, null); const clonedObj = history.state; history.replaceState(oldState, null); return clonedObj; }; 

Esempio di utilizzo:

 'use strict'; const main = () => { const original = { date: new Date(), number: Math.random() }; original.self = original; const clone = structuredClone(original); // They're different objects: console.assert(original !== clone); console.assert(original.date !== clone.date); // They're cyclical: console.assert(original.self === original); console.assert(clone.self === clone); // They contain equivalent values: console.assert(original.number === clone.number); console.assert(Number(original.date) === Number(clone.date)); console.log("Assertions complete."); }; const structuredClone = obj => { const oldState = history.state; history.replaceState(obj, null); const clonedObj = history.state; history.replaceState(oldState, null); return clonedObj; }; main(); 

Se non ce n’era uno integrato, potresti provare:

  function clone(obj) { if (obj === null || typeof(obj) !== 'object' || 'isActiveClone' in obj) return obj; if (obj instanceof Date) var temp = new obj.constructor(); //or new Date(obj); else var temp = obj.constructor(); for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { obj['isActiveClone'] = null; temp[key] = clone(obj[key]); delete obj['isActiveClone']; } } return temp; } 

Il modo efficace per clonare (non clonare in profondità) un object in una riga di codice

Un metodo Object.assign fa parte dello standard ECMAScript 2015 (ES6) e fa esattamente ciò di cui hai bisogno.

 var clone = Object.assign({}, obj); 

Il metodo Object.assign () viene utilizzato per copiare i valori di tutte le proprietà enumerabili di uno o più oggetti di origine in un object di destinazione.

Leggi di più…

Il polyfill per supportare i browser più vecchi:

 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; } }); } 

Codice:

 // extends 'from' object with members from 'to'. If 'to' is null, a deep clone of 'from' is returned function extend(from, to) { if (from == null || typeof from != "object") return from; if (from.constructor != Object && from.constructor != Array) return from; if (from.constructor == Date || from.constructor == RegExp || from.constructor == Function || from.constructor == String || from.constructor == Number || from.constructor == Boolean) return new from.constructor(from); to = to || new from.constructor(); for (var name in from) { to[name] = typeof to[name] == "undefined" ? extend(from[name], null) : to[name]; } return to; } 

Test:

 var obj = { date: new Date(), func: function(q) { return 1 + q; }, num: 123, text: "asdasd", array: [1, "asd"], regex: new RegExp(/aaa/i), subobj: { num: 234, text: "asdsaD" } } var clone = extend(obj); 

Questo è quello che sto usando:

 function cloneObject(obj) { var clone = {}; for(var i in obj) { if(typeof(obj[i])=="object" && obj[i] != null) clone[i] = cloneObject(obj[i]); else clone[i] = obj[i]; } return clone; } 

Copia approfondita per rendimento: classificata dal migliore al peggiore

  • Reassignment “=” (array di stringhe, array di numeri – solo)
  • Slice (array di stringhe, array di numeri – solo)
  • Concatenazione (array di stringhe, array di numeri – solo)
  • Funzione personalizzata: copia per ciclo o ricorsiva
  • $ $ estensione di jQuery
  • JSON.parse (array di stringhe, array di numeri, matrici di oggetti – solo)
  • _.Clone (array di stringhe, array di numeri di Underscore.js – solo)
  • _.CloneDeep di Lo-Dash

Copia in profondità una serie di stringhe o numeri (un livello – nessun riferimento puntatore):

Quando un array contiene numeri e stringhe – funzioni come .slice (), .concat (), .splice (), l’operatore di assegnazione “=” e la funzione clone di Underscore.js; farà una copia profonda degli elementi dell’array.

Dove la riassegnazione ha le prestazioni più veloci:

 var arr1 = ['a', 'b', 'c']; var arr2 = arr1; arr1 = ['a', 'b', 'c']; 

E .slice () ha prestazioni migliori di .concat (), http://jsperf.com/duplicate-array-slice-vs-concat/3

 var arr1 = ['a', 'b', 'c']; // Becomes arr1 = ['a', 'b', 'c'] var arr2a = arr1.slice(0); // Becomes arr2a = ['a', 'b', 'c'] - deep copy var arr2b = arr1.concat(); // Becomes arr2b = ['a', 'b', 'c'] - deep copy 

Copia in profondità una serie di oggetti (due o più livelli – puntatori di riferimento):

 var arr1 = [{object:'a'}, {object:'b'}]; 

Scrivi una funzione personalizzata (ha prestazioni più veloci di $ .extend () o JSON.parse):

 function copy(o) { var out, v, key; out = Array.isArray(o) ? [] : {}; for (key in o) { v = o[key]; out[key] = (typeof v === "object" && v !== null) ? copy(v) : v; } return out; } copy(arr1); 

Utilizzare le funzioni di utilità di terze parti:

 $.extend(true, [], arr1); // Jquery Extend JSON.parse(arr1); _.cloneDeep(arr1); // Lo-dash 

Dove $ .extend di jQuery ha prestazioni migliori:

 var clone = function() { var newObj = (this instanceof Array) ? [] : {}; for (var i in this) { if (this[i] && typeof this[i] == "object") { newObj[i] = this[i].clone(); } else { newObj[i] = this[i]; } } return newObj; }; Object.defineProperty( Object.prototype, "clone", {value: clone, enumerable: false}); 

So che questo è un vecchio post, ma ho pensato che questo potrebbe essere di aiuto per la prossima persona che inciampa.

Finché non assegni un object a qualcosa, non mantiene alcun riferimento in memoria. Quindi per creare un object che vuoi condividere tra altri oggetti, dovrai creare una fabbrica in questo modo:

 var a = function(){ return { father:'zacharias' }; }, b = a(), c = a(); c.father = 'johndoe'; alert(b.father); 

C’è una biblioteca (chiamata “clone”) , che funziona abbastanza bene. Fornisce la clonazione / copia ricorsiva più completa di oggetti arbitrari che io conosca. Supporta anche riferimenti circolari, che non sono ancora coperti dalle altre risposte.

Lo puoi trovare anche su npm . Può essere utilizzato per il browser e Node.js.

Ecco un esempio su come usarlo:

Installalo con

 npm install clone 

o confezionarlo con Ender .

 ender build clone [...] 

Puoi anche scaricare il codice sorgente manualmente.

Quindi puoi usarlo nel tuo codice sorgente.

 var clone = require('clone'); var a = { foo: { bar: 'baz' } }; // inital value of a var b = clone(a); // clone a -> b a.foo.bar = 'foo'; // change a console.log(a); // { foo: { bar: 'foo' } } console.log(b); // { foo: { bar: 'baz' } } 

(Disclaimer: sono l’autore della biblioteca.)

Se lo stai usando, la libreria Underscore.js ha un metodo clone .

 var newObject = _.clone(oldObject); 

Cloning un object era sempre una preoccupazione in JS, ma era tutto prima di ES6, elencherò diversi modi di copiare un object in JavaScript qui sotto, immagina di avere l’object sotto e vorrei avere una copia profonda di quello:

 var obj = {a:1, b:2, c:3, d:4}; 

Ci sono alcuni modi per copiare questo object, senza cambiare l’origine:

1) ES5 +, usando una semplice funzione per fare la copia per te:

 function deepCopyObj(obj) { if (null == obj || "object" != typeof obj) return obj; if (obj instanceof Date) { var copy = new Date(); copy.setTime(obj.getTime()); return copy; } if (obj instanceof Array) { var copy = []; for (var i = 0, len = obj.length; i < len; i++) { copy[i] = cloneSO(obj[i]); } return copy; } 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 this object."); } 

2) ES5 +, utilizzando JSON.parse e JSON.stringify.

 var deepCopyObj = JSON.parse(JSON.stringify(obj)); 

3) AngularJs:

 var deepCopyObj = angular.copy(obj); 

4) jQuery:

 var deepCopyObj = jQuery.extend(true, {}, obj); 

5) UnderscoreJs e Loadash:

 var deepCopyObj = _.cloneDeep(obj); //latest version UndescoreJs makes shallow copy 

Spero che questi aiuti ...

Ecco una versione della risposta di ConroyP sopra che funziona anche se il costruttore ha richiesto i parametri:

 //If Object.create isn't already defined, we just do the simple shim, //without the second argument, since that's all we need here var object_create = Object.create; if (typeof object_create !== 'function') { object_create = function(o) { function F() {} F.prototype = o; return new F(); }; } function deepCopy(obj) { if(obj == null || typeof(obj) !== 'object'){ return obj; } //make sure the returned object has the same prototype as the original var ret = object_create(obj.constructor.prototype); for(var key in obj){ ret[key] = deepCopy(obj[key]); } return ret; } 

This function is also available in my simpleoo library.

Modificare:

Here’s a more robust version (thanks to Justin McCandless this now supports cyclic references as well):

 /** * Deep copy an object (make copies of all its object properties, sub-properties, etc.) * An improved version of http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone * that doesn't break if the constructor has required parameters * * It also borrows some code from http://stackoverflow.com/a/11621004/560114 */ function deepCopy(src, /* INTERNAL */ _visited, _copiesVisited) { if(src === null || typeof(src) !== 'object'){ return src; } //Honor native/custom clone methods if(typeof src.clone == 'function'){ return src.clone(true); } //Special cases: //Date if(src instanceof Date){ return new Date(src.getTime()); } //RegExp if(src instanceof RegExp){ return new RegExp(src); } //DOM Element if(src.nodeType && typeof src.cloneNode == 'function'){ return src.cloneNode(true); } // Initialize the visited objects arrays if needed. // This is used to detect cyclic references. if (_visited === undefined){ _visited = []; _copiesVisited = []; } // Check if this object has already been visited var i, len = _visited.length; for (i = 0; i < len; i++) { // If so, get the copy we already made if (src === _visited[i]) { return _copiesVisited[i]; } } //Array if (Object.prototype.toString.call(src) == '[object Array]') { //[].slice() by itself would soft clone var ret = src.slice(); //add it to the visited array _visited.push(src); _copiesVisited.push(ret); var i = ret.length; while (i--) { ret[i] = deepCopy(ret[i], _visited, _copiesVisited); } return ret; } //If we've reached here, we have a regular object //make sure the returned object has the same prototype as the original var proto = (Object.getPrototypeOf ? Object.getPrototypeOf(src): src.__proto__); if (!proto) { proto = src.constructor.prototype; //this line would probably only be reached by very old browsers } var dest = object_create(proto); //add this object to the visited array _visited.push(src); _copiesVisited.push(dest); for (var key in src) { //Note: this does NOT preserve ES5 property attributes like 'writable', 'enumerable', etc. //For an example of how this could be modified to do so, see the singleMixin() function dest[key] = deepCopy(src[key], _visited, _copiesVisited); } return dest; } //If Object.create isn't already defined, we just do the simple shim, //without the second argument, since that's all we need here var object_create = Object.create; if (typeof object_create !== 'function') { object_create = function(o) { function F() {} F.prototype = o; return new F(); }; } 

The following creates two instances of the same object. I found it and am using it currently. It’s simple and easy to use.

 var objToCreate = JSON.parse(JSON.stringify(cloneThis)); 
 function clone(obj) { var clone = {}; clone.prototype = obj.prototype; for (property in obj) clone[property] = obj[property]; return clone; } 

Crockford suggests (and I prefer) using this function:

 function object(o) { function F() {} F.prototype = o; return new F(); } var newObject = object(oldObject); 

It’s terse, works as expected and you don’t need a library.


MODIFICARE:

This is a polyfill for Object.create , so you also can use this.

 var newObject = Object.create(oldObject); 

NOTE: If you use some of this, you may have problems with some iteration who use hasOwnProperty . Because, create create new empty object who inherits oldObject . But it is still useful and practical for cloning objects.

For exemple if oldObject.a = 5;

 newObject.a; // is 5 

but:

 oldObject.hasOwnProperty(a); // is true newObject.hasOwnProperty(a); // is false 

Lodash has a nice _.cloneDeep(value) method:

 var objects = [{ 'a': 1 }, { 'b': 2 }]; var deep = _.cloneDeep(objects); console.log(deep[0] === objects[0]); // => false 

Shallow copy one-liner ( ECMAScript 5th edition ):

 var origin = { foo : {} }; var copy = Object.keys(origin).reduce(function(c,k){c[k]=origin[k];return c;},{}); console.log(origin, copy); console.log(origin == copy); // false console.log(origin.foo == copy.foo); // true 

And shallow copy one-liner ( ECMAScript 6th edition , 2015):

 var origin = { foo : {} }; var copy = Object.assign({}, origin); console.log(origin, copy); console.log(origin == copy); // false console.log(origin.foo == copy.foo); // true 

Just because I didn’t see AngularJS mentioned and thought that people might want to know…

angular.copy also provides a method of deep copying objects and arrays.

There seems to be no ideal deep clone operator yet for array-like objects. As the code below illustrates, John Resig’s jQuery cloner turns arrays with non-numeric properties into objects that are not arrays, and RegDwight’s JSON cloner drops the non-numeric properties. The following tests illustrate these points on multiple browsers:

 function jQueryClone(obj) { return jQuery.extend(true, {}, obj) } function JSONClone(obj) { return JSON.parse(JSON.stringify(obj)) } var arrayLikeObj = [[1, "a", "b"], [2, "b", "a"]]; arrayLikeObj.names = ["m", "n", "o"]; var JSONCopy = JSONClone(arrayLikeObj); var jQueryCopy = jQueryClone(arrayLikeObj); alert("Is arrayLikeObj an array instance?" + (arrayLikeObj instanceof Array) + "\nIs the jQueryClone an array instance? " + (jQueryCopy instanceof Array) + "\nWhat are the arrayLikeObj names? " + arrayLikeObj.names + "\nAnd what are the JSONClone names? " + JSONCopy.names) 

I have two good answers depending on whether your objective is to clone a “plain old JavaScript object” or not.

Let’s also assume that your intention is to create a complete clone with no prototype references back to the source object. If you’re not interested in a complete clone, then you can use many of the Object.clone() routines provided in some of the other answers (Crockford’s pattern).

For plain old JavaScript objects, a tried and true good way to clone an object in modern runtimes is quite simply:

 var clone = JSON.parse(JSON.stringify(obj)); 

Note that the source object must be a pure JSON object. This is to say, all of its nested properties must be scalars (like boolean, string, array, object, etc). Any functions or special objects like RegExp or Date will not be cloned.

Is it efficient? Heck yes. We’ve tried all kinds of cloning methods and this works best. I’m sure some ninja could conjure up a faster method. But I suspect we’re talking about marginal gains.

This approach is just simple and easy to implement. Wrap it into a convenience function and if you really need to squeeze out some gain, go for at a later time.

Now, for non-plain JavaScript objects, there isn’t a really simple answer. In fact, there can’t be because of the dynamic nature of JavaScript functions and inner object state. Deep cloning a JSON structure with functions inside requires you recreate those functions and their inner context. And JavaScript simply doesn’t have a standardized way of doing that.

The correct way to do this, once again, is via a convenience method that you declare and reuse within your code. The convenience method can be endowed with some understanding of your own objects so you can make sure to properly recreate the graph within the new object.

We’re written our own, but the best general approach I’ve seen is covered here:

http://davidwalsh.name/javascript-clone

This is the right idea. The author (David Walsh) has commented out the cloning of generalized functions. This is something you might choose to do, depending on your use case.

The main idea is that you need to special handle the instantiation of your functions (or prototypal classs, so to speak) on a per-type basis. Here, he’s provided a few examples for RegExp and Date.

Not only is this code brief, but it’s also very readable. It’s pretty easy to extend.

Is this efficient? Heck yes. Given that the goal is to produce a true deep-copy clone, then you’re going to have to walk the members of the source object graph. With this approach, you can tweak exactly which child members to treat and how to manually handle custom types.

So there you go. Two approaches. Both are efficient in my view.

This isn’t generally the most efficient solution, but it does what I need. Simple test cases below…

 function clone(obj, clones) { // Makes a deep copy of 'obj'. Handles cyclic structures by // tracking cloned obj's in the 'clones' parameter. Functions // are included, but not cloned. Functions members are cloned. var new_obj, already_cloned, t = typeof obj, i = 0, l, pair; clones = clones || []; if (obj === null) { return obj; } if (t === "object" || t === "function") { // check to see if we've already cloned obj for (i = 0, l = clones.length; i < l; i++) { pair = clones[i]; if (pair[0] === obj) { already_cloned = pair[1]; break; } } if (already_cloned) { return already_cloned; } else { if (t === "object") { // create new object new_obj = new obj.constructor(); } else { // Just use functions as is new_obj = obj; } clones.push([obj, new_obj]); // keep track of objects we've cloned for (key in obj) { // clone object members if (obj.hasOwnProperty(key)) { new_obj[key] = clone(obj[key], clones); } } } } return new_obj || obj; } 

Cyclic array test...

 a = [] a.push("b", "c", a) aa = clone(a) aa === a //=> false aa[2] === a //=> false aa[2] === a[2] //=> false aa[2] === aa //=> true 

Function test...

 f = new Function fa = a ff = clone(f) ff === f //=> true ff.a === a //=> false 

AngularJS

Well if you’re using angular you could do this too

 var newObject = angular.copy(oldObject); 

I disagree with the answer with the greatest votes here . A Recursive Deep Clone is much faster than the JSON.parse(JSON.stringify(obj)) approach mentioned.

And here’s the function for quick reference:

 function cloneDeep (o) { let newO let i if (typeof o !== 'object') return o if (!o) return o if (Object.prototype.toString.apply(o) === '[object Array]') { newO = [] for (i = 0; i < o.length; i += 1) { newO[i] = cloneDeep(o[i]) } return newO } newO = {} for (i in o) { if (o.hasOwnProperty(i)) { newO[i] = cloneDeep(o[i]) } } return newO } 
 // obj target object, vals source object var setVals = function (obj, vals) { if (obj && vals) { for (var x in vals) { if (vals.hasOwnProperty(x)) { if (obj[x] && typeof vals[x] === 'object') { obj[x] = setVals(obj[x], vals[x]); } else { obj[x] = vals[x]; } } } } return obj; }; 

Here is a comprehensive clone() method that can clone any JavaScript object. It handles almost all the cases:

 function clone(src, deep) { var toString = Object.prototype.toString; if (!src && typeof src != "object") { // Any non-object (Boolean, String, Number), null, undefined, NaN return src; } // Honor native/custom clone methods if (src.clone && toString.call(src.clone) == "[object Function]") { return src.clone(deep); } // DOM elements if (src.nodeType && toString.call(src.cloneNode) == "[object Function]") { return src.cloneNode(deep); } // Date if (toString.call(src) == "[object Date]") { return new Date(src.getTime()); } // RegExp if (toString.call(src) == "[object RegExp]") { return new RegExp(src); } // Function if (toString.call(src) == "[object Function]") { //Wrap in another method to make sure == is not true; //Note: Huge performance issue due to closures, comment this :) return (function(){ src.apply(this, arguments); }); } var ret, index; //Array if (toString.call(src) == "[object Array]") { //[].slice(0) would soft clone ret = src.slice(); if (deep) { index = ret.length; while (index--) { ret[index] = clone(ret[index], true); } } } //Object else { ret = src.constructor ? new src.constructor() : {}; for (var prop in src) { ret[prop] = deep ? clone(src[prop], true) : src[prop]; } } return ret; }; 

I usually use var newObj = JSON.parse( JSON.stringify(oldObje) ); but, here’s a more proper way:

 var o = {}; var oo = Object.create(o); (o === oo); // => false 

Watch legacy browsers!

Only when you can use ECMAScript 6 or transpilers .

Caratteristiche:

  • Won’t trigger getter/setter while copying.
  • Preserves getter/setter.
  • Preserves prototype informations.
  • Works with both object-literal and functional OO writing styles.

Codice:

 function clone(target, source){ for(let key in source){ // Use getOwnPropertyDescriptor instead of source[key] to prevent from trigering setter/getter. let descriptor = Object.getOwnPropertyDescriptor(source, key); if(descriptor.value instanceof String){ target[key] = new String(descriptor.value); } else if(descriptor.value instanceof Array){ target[key] = clone([], descriptor.value); } else if(descriptor.value instanceof Object){ let prototype = Reflect.getPrototypeOf(descriptor.value); let cloneObject = clone({}, descriptor.value); Reflect.setPrototypeOf(cloneObject, prototype); target[key] = cloneObject; } else { Object.defineProperty(target, key, descriptor); } } let prototype = Reflect.getPrototypeOf(source); Reflect.setPrototypeOf(target, prototype); return target; }