¿Cuál es la forma más eficiente de clonar profundamente un objeto en javascript?

¿Cuál es la forma más eficiente de clonar un objeto JavaScript? He visto obj = eval(uneval(o)); pero que no es estándar y solo es compatible con Firefox .

He hecho cosas como obj = JSON.parse(JSON.stringify(o)); , pero dudo de la efectividad.

También vi las funciones de copia recursiva con varios defectos.

Me sorprende que no haya una solución canónica.

4845
23 сент. fijado por jschrab el 23 sep . 2008-09-23 19:26 '08 a las 7:26 pm 2008-09-23 19:26
@ 69 respuestas
  • 1
  • 2
  • 3

Nota: Esta es la respuesta a otra respuesta, no la respuesta correcta a esta pregunta. Si desea clonar objetos rápidamente, siga los consejos de Corban en su respuesta a esta pregunta.


Quiero señalar que el método .clone() en jQuery solo clona elementos DOM. Para clonar objetos de JavaScript, debes:

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

Se puede encontrar más información en la documentación de jQuery .

También quiero señalar que una copia profunda es en realidad mucho más inteligente de lo que se muestra arriba, puede evitar muchas trampas (por ejemplo, para expandir profundamente el elemento DOM). Se usa a menudo en el núcleo de jQuery y en complementos con gran efecto.

4203
23 сент. Respuesta dada por John Resig el 23 de septiembre. 2008-09-23 21:09 '08 a las 9:09 pm 2008-09-23 21:09

Echa un vistazo a este punto de referencia: http://jsben.ch/#/bWfk9

En mis pruebas anteriores, donde la velocidad era el problema principal, descubrí

<Prev> <code> JSON.parse (JSON.stringify (OBJ)) Código>

para ser la forma más rápida de clonar profundamente un objeto (supera a jQuery.extend con la marca de profundidad establecida en un 10-20%).

jQuery.extend es bastante rápido cuando el indicador de valor profundo se establece en falso (clon superficial). Esta es una buena opción, ya que incluye lógica adicional para la comprobación de tipos y no copia las propiedades de undefined, etc. Pero también le ralentizará un poco.

Si conoce la estructura de los objetos que está intentando clonar o puede evitar las matrices anidadas profundas, puede escribir un bucle for (var я in obj) simple for (var я in obj) para clonar su objeto, verificando la propiedad de propiedad de la propiedad y será mucho más rápido que jQuery.

border=0

Finalmente, si está intentando clonar una estructura de objeto conocida en un bucle activo, puede obtener MUCHO MÁS MÁS RENDIMIENTO simplemente insertando el procedimiento de clonación y creando el objeto manualmente.

Los mecanismos de seguimiento de JavaScript apestan al optimizar para for..in bucles y comprobar que hasOwnProperty también lo ralentizará. Clonación manual cuando la velocidad es una necesidad absoluta.

  var clonedObject = { knownProp: obj.knownProp,.. } Код> 

Tenga cuidado con el uso del método JSON.parse(JSON.stringify(obj)) para los objetos Date - JSON.stringify(новая дата()) devuelve una representación de cadena de la fecha en formato ISO, en la que JSON.parse() no vuelve al objeto Date . Ver esta respuesta para más detalles .

Además, tenga en cuenta que en Chrome 65, al menos, la clonación nativa no es adecuada. De acuerdo con este JSPerf , hacer su propia clonación al crear una nueva función es casi 800x más lento que usar JSON.stringify, que pasa increíblemente rápido por todo el tablero.

2046
17 марта '11 в 22:19 2011-03-17 22:19 Corban Brook da la respuesta el 17 de marzo de 2011 a las 10:19 p.m. 2011-03-17 22:19

Suponiendo que solo tiene variables y no funciones en su objeto, simplemente puede usar:

 var newObject = JSON.parse(JSON.stringify(oldObject)); 
431
04 янв. La respuesta es dada por el Sultán Shakir el 4 de enero. 2011-01-04 11:05 '11 a las 11:05 2011-01-04 11:05

Clonación estructurada

El estándar HTML incluye un algoritmo de clonación / serialización estructurado interno que puede crear clones profundos de objetos. Todavía está limitado a ciertos tipos incorporados, pero además de varios tipos compatibles con JSON, también admite fechas, RegExps, mapas, conjuntos, blobs, listas de archivos, ImageDatas, matrices dispersas, matrices tipadas y posiblemente más en el futuro. ., También almacena referencias en los datos clonados, lo que le permite mantener estructuras cíclicas y recursivas que pueden causar errores para JSON.

Soporte en Node.js: experimental 🙂

El módulo v8 en Node.js actualmente (a partir del Nodo 11) proporciona directamente una API de serialización estructurada , pero esta funcionalidad aún está marcada como "experimental" y se puede cambiar o eliminar en futuras versiones. Si está utilizando una versión compatible, clonar un objeto es tan simple como:

06 июня '12 в 17:59 2012-06-06 17:59 La respuesta se da a Jeremy Banks 06 de junio de 2012 a las 17:59 2012-06-06 17:59

Si no estuviera integrado, puedes intentar:

 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; } 
294
23 сент. Respuesta dada por ConroyP el 23 de septiembre. 2008-09-23 19:38 '08 a las 7:38 pm 2008-09-23 19:38

Manera efectiva de clonar (no clonar en profundidad) un objeto en una línea de código

El método Object.assign es parte del estándar ECMAScript 2015 (ES6) y hace exactamente lo que necesita.

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

El método Object.assign () se usa para copiar los valores de todas las propiedades correctas enumeradas de uno o más objetos de origen al objeto de destino.

Leer más ...

polyfill para soportar navegadores más antiguos:

 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; } }); } 
148
15 дек. La respuesta la da Eugene Tiurin el 15 de diciembre. 2015-12-15 10:26 '15 a las 10:26 AM 2015-12-15 10:26

código:

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

Prueba:

 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); 
94
25 июня '09 в 10:53 2009-06-25 10:53 Kamarey da la respuesta el 25 de junio de 2009 a las 10:53, 2009-06-25 10:53

Esto es lo que yo uso:

 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; } 
86
12 дек. La respuesta la da Alan 12 dic. 2009-12-12 01:47 '09 a 1:47 2009-12-12 01:47

Copia profunda: de mejor a peor

  • Reasignación "=" (matrices de cadenas, matrices numéricas - solo)
  • Rebanada (matrices de cadenas, matrices de números - solamente)
  • Concatenación (solo matrices de cadenas, matrices numéricas)
  • Función personalizada: for-loop o copia recursiva.
  • jQuery $ .extend
  • JSON.parse (solo matrices de cadenas, matrices de números, matrices de objetos)
  • Underscore.js _.clone (solo matrices de cadenas, matrices numéricas)
  • Lo-Dash _.cloneDeep

Copie profundamente una matriz de cadenas o números (un nivel, sin punteros):

Cuando una matriz contiene números y cadenas, funciones como.slice (), Concat (),. Splice (), el operador de asignación "=" y la función de clonación Underscore.js; Haga una copia profunda de los elementos de la matriz.

Si la reasignación tiene el rendimiento más alto:

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

I.slice () tiene mejor rendimiento que .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 

Copie profundamente una matriz de objetos (dos o más niveles - punteros):

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

Escriba una función personalizada (tiene mejor rendimiento que $ .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); 

Utilice funciones de utilidad de terceros:

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

Donde jQuery $ .extend tiene un mejor rendimiento:

71
18 сент. La respuesta se da tmontmontague 18 sep . 2014-09-18 23:10 '14 a las 23:10 2014-09-18 23:10
 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}); 
60
26 дек. La respuesta la da Zibri el 26 de diciembre. 2009-12-26 17:59 '09 a las 17:59 2009-12-26 17:59

Sé que este es un post antiguo, pero pensé que podría ayudar a alguien que tropieza.

Mientras no asigne un objeto a algo, no mantiene una referencia en la memoria. Por lo tanto, para crear un objeto que desee compartir entre otros objetos, debe crear una fábrica como esta:

 var a = function(){ return { father:'zacharias' }; }, b = a(), c = a(); c.father = 'johndoe'; alert(b.father); 
53
24 сент. respuesta dada por Joe 24 sep. 2011-09-24 22:28 '11 a las 22:28 2011-09-24 22:28

Cloning objetos siempre ha sido un motivo de preocupación en JS, pero antes de ES6, enumero varias formas de copiar un objeto en JavaScript a continuación, imagina que tienes un objeto a continuación y deseas tener una copia profunda de eso:

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

Hay varias formas de copiar este objeto sin cambiar la fuente:

1) ES5 + usando una función simple para copiar:

 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 + utilizando JSON.parse y 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) Underscore Js y Loadash:

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

Espero que esta ayuda ...

52
03 апр. La respuesta la da Alireza 03 abr. 2017-04-03 18:37 '17 a las 6:37 pm 2017-04-03 18:37

Hay una biblioteca (llamada "clon") , que la hace bastante buena. Proporciona la clonación / copia recursiva más completa de objetos arbitrarios que conozco. También admite enlaces circulares que aún no están cubiertos por otras respuestas.

Lo puedes encontrar en npm . Se puede usar tanto para el navegador como para Node.js.

Aquí hay un ejemplo de cómo usarlo:

Instalarlo con

 npm install clone 

o empáquelo con Ender .

 ender build clone [...] 

También puede descargar el código fuente manualmente.

Entonces puedes usarlo en tu código fuente.

 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' } } 

(Descargo de responsabilidad: Soy el autor de la biblioteca.)

51
17 окт. La respuesta se da pvorb 17 oct. 2012-10-17 21:36 '12 a las 21:36 2012-10-17 21:36

Si lo usas, la biblioteca Underscore.js tiene un clon .

 var newObject = _.clone(oldObject); 
48
15 дек. La respuesta está dada por itadok 15 dic. 2011-12-15 18:56 '11 a las 18:56 2011-12-15 18:56

Aquí está la versión anterior de ConroyP, que funciona incluso si el diseñador requiere parámetros:

 //If Object.create isn't already defined, we just do the simple shim, //without the second argument, since that 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; } 

Esta característica también está disponible en mi biblioteca simpleoo .

Editar:

Aquí hay una versión más confiable (gracias a Justin McCandles, ahora es compatible con referencias circulares):

  function deepCopy(src,  _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 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(); }; } 
36
11 нояб. Respuesta dada por Matt Browne 11 nov. 2012-11-11 20:53 '12 a las 8:53 pm 2012-11-11 20:53

Lo siguiente crea dos instancias del mismo objeto. Lo encontré y lo uso ahora. Es simple y fácil de usar.

 var objToCreate = JSON.parse(JSON.stringify(cloneThis)); 
31
21 авг. Respuesta dada por nathan rogers 21 de agosto 2015-08-21 18:51 '15 a las 18:51 2015-08-21 18:51

Copia profunda de objetos en javascript (creo que el mejor y más fácil)

1. Usando JSON.parse (JSON.stringify (objeto));

 var obj = { a: 1, b: { c: 2 } } var newObj = JSON.parse(JSON.stringify(obj)); obj.bc = 20; console.log(obj); // { a: 1, b: { c: 20 } } console.log(newObj); // { a: 1, b: { c: 2 } } 

2. Utilizando el método creado.

 function cloneObject(obj) { var clone = {}; for(var i in obj) { if(obj[i] != null  typeof(obj[i])=="object") clone[i] = cloneObject(obj[i]); else clone[i] = obj[i]; } return clone; } var obj = { a: 1, b: { c: 2 } } var newObj = cloneObject(obj); obj.bc = 20; console.log(obj); // { a: 1, b: { c: 20 } } console.log(newObj); // { a: 1, b: { c: 2 } } 

3. Usando Lo-Dash _.cloneDeep link lodash

 var obj = { a: 1, b: { c: 2 } } var newObj = _.cloneDeep(obj); obj.bc = 20; console.log(obj); // { a: 1, b: { c: 20 } } console.log(newObj); // { a: 1, b: { c: 2 } } 

4. Usando Object.assign ()

 var obj = { a: 1, b: 2 } var newObj = _.clone(obj); obj.b = 20; console.log(obj); // { a: 1, b: 20 } console.log(newObj); // { a: 1, b: 2 } 

Pero mal cuando

 var obj = { a: 1, b: { c: 2 } } var newObj = Object.assign({}, obj); obj.bc = 20; console.log(obj); // { a: 1, b: { c: 20 } } console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG // Note: Properties on the prototype chain and non-enumerable properties cannot be copied. 

5. Utilizando Underscore.js _.clone link Underscore.js

 var obj = { a: 1, b: 2 } var newObj = _.clone(obj); obj.b = 20; console.log(obj); // { a: 1, b: 20 } console.log(newObj); // { a: 1, b: 2 } 

Pero mal cuando

 var obj = { a: 1, b: { c: 2 } } var newObj = _.cloneDeep(obj); obj.bc = 20; console.log(obj); // { a: 1, b: { c: 20 } } console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG // (Create a shallow-copied clone of the provided plain object. Any nested objects or arrays will be copied by reference, not duplicated.) 

Enlace medium.com

JSBEN.CH Performance Benchmarking Playground 1 ~ 3 http://jsben.ch/KVQLd 2019

08 авг. La respuesta se da TinhNQ 08 ago. 2018-08-08 11:17 '18 a las 11:17 ; 2018-08-08 11:17

Lodash tiene un buen _ método . cloneDeep (valor) :

 var objects = [{ 'a': 1 }, { 'b': 2 }]; var deep = _.cloneDeep(objects); console.log(deep[0] === objects[0]); // => false 
23
22 июня '13 в 18:03 2013-06-22 18:03 La respuesta está dada por opensas el 22 de junio de 2013 a las 18:03 2013-06-22 18:03

Crockford sugiere (y prefiero) usar esta característica:

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

Es corto, funciona como se esperaba y no necesita una biblioteca.


EDITAR:

Este es un polyfill para Object.create , por lo que también puede usarlo.

 var newObject = Object.create(oldObject); 

NOTA. . Si usa algunos de ellos, puede tener problemas con alguna iteración, que usa hasOwnProperty . Porque create crea un nuevo objeto vacío que hereda oldObject . Pero sigue siendo útil y práctico para clonar objetos.

Por ejemplo, si oldObject.a = 5;

 newObject.a; // is 5 

un

 oldObject.hasOwnProperty(a); // is true newObject.hasOwnProperty(a); // is false 
23
06 окт. respuesta dada por Chris Broski 06 de octubre 2010-10-06 18:08 '10 a las 18:08 2010-10-06 18:08
 function clone(obj) { var clone = {}; clone.prototype = obj.prototype; for (property in obj) clone[property] = obj[property]; return clone; } 
22
23 сент. Respuesta dada por Mark Cidade el 23 de septiembre. 2008-09-23 19:45 '08 a las 7:45 pm 2008-09-23 19:45

Copia de una sola línea de copia superficial ( ECMAScript 5ª edición ):

 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 

Copia única y pequeña ( ECMAScript 6ª edición , 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 
20
05 июля '12 в 0:44 2012-07-05 00:44 La respuesta la da Maël Nison el 5 de julio de 2014 a las 12:44 2012-07-05 00:44

Solo porque no vi AngularJS y pensé que la gente querría saber ...

angular.copy también proporciona un método para copiar en profundidad objetos y matrices.

17
14 мая '16 в 1:16 2016-05-14 01:16 Dan Atkinson da la respuesta el 14 de mayo de 2016 a las 1:16 2016-05-14 01:16

Parece que no hay un operador ideal de clonación profunda para objetos del tipo de matriz. Как видно из приведенного ниже кода, клонирующий JQuery John Resig превращает массивы с нечисловыми свойствами в объекты, которые не являются массивами, а клонирование RegDwight JSON отбрасывает нечисловые свойства. Следующие тесты иллюстрируют эти моменты в нескольких браузерах:

 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) 
16
ответ дан Page Notes 17 окт. '10 в 7:01 2010-10-17 07:01