¿Cuál es la forma más eficiente de clonar un objeto JavaScript? He visto que obj = eval(uneval(o));
se usa, pero eso no es estándar y solo es compatible con Firefox .
He hecho cosas como obj = JSON.parse(JSON.stringify(o));
pero cuestionar la eficiencia.
También he visto funciones de copia recursiva con varios defectos.
Me sorprende que no exista una solución canónica.
javascript
object
clone
jschrab
fuente
fuente
eval()
generalmente es una mala idea porque muchos optimizadores del motor Javascript tienen que apagarse cuando se manejan variables que se configuran a través deeval
. Solo tenereval()
en su código puede conducir a un peor rendimiento.JSON
método perderá cualquier tipo de Javascript que no tenga equivalente en JSON. Por ejemplo:JSON.parse(JSON.stringify({a:null,b:NaN,c:Infinity,d:undefined,e:function(){},f:Number,g:false}))
generará{a: null, b: null, c: null, g: false}
Respuestas:
Clonación profunda nativa
Se llama "clonación estructurada", funciona experimentalmente en el Nodo 11 y posteriores, y con suerte aterrizará en los navegadores. Vea esta respuesta para más detalles.
Clonación rápida con pérdida de datos - JSON.parse / stringify
Si no se utiliza
Date
s, funcionesundefined
,Infinity
, expresiones regulares, Mapas, Conjuntos, gotas, filelists, ImageDatas, matrices dispersas, Conjuntos escritos u otros tipos complejos dentro de su objeto, un revestimiento muy simple uno a lo profundo del clon de un objeto es:JSON.parse(JSON.stringify(object))
Ver la respuesta de Corban para puntos de referencia.
Clonación confiable usando una biblioteca
Dado que la clonación de objetos no es trivial (tipos complejos, referencias circulares, funciones, etc.), la mayoría de las bibliotecas principales proporcionan funciones para clonar objetos. No reinvente la rueda : si ya está utilizando una biblioteca, verifique si tiene una función de clonación de objetos. Por ejemplo,
cloneDeep
; se puede importar por separado a través del módulo lodash.clonedeep y es probablemente su mejor opción si aún no está utilizando una biblioteca que proporciona una función de clonación profundaangular.copy
jQuery.extend(true, { }, oldObject)
;.clone()
solo clona elementos DOMES6
Para completar, tenga en cuenta que ES6 ofrece dos mecanismos de copia superficial:
Object.assign()
y la sintaxis de propagación . que copia los valores de todas las propiedades propias enumerables de un objeto a otro. Por ejemplo:fuente
.clone()
, que no es el código correcto para ser utilizando en este contexto). Desafortunadamente, esta pregunta ha pasado por tantas revisiones que la discusión original ya no es aparente. Por favor, solo siga los consejos de Corban y escriba un bucle o copie las propiedades directamente a un nuevo objeto, si le importa la velocidad. ¡O pruébalo por ti mismo!Consulte este punto de referencia: http://jsben.ch/#/bWfk9
En mis pruebas anteriores, donde la velocidad era una preocupación principal, encontré
para ser la forma más lenta de clonar en profundidad un objeto (es más lento que jQuery.extend con el
deep
indicador establecido verdadero en un 10-20%).jQuery.extend es bastante rápido cuando la
deep
bandera se establece enfalse
(clon superficial). Es una buena opción, porque incluye algo de lógica adicional para la validación de tipos y no copia sobre propiedades indefinidas, etc., pero esto también lo ralentizará un poco.Si conoce la estructura de los objetos que intenta clonar o puede evitar matrices anidadas profundas, puede escribir un
for (var i in obj)
bucle simple para clonar su objeto mientras verifica hasOwnProperty y será mucho más rápido que jQuery.Por último, si está intentando clonar una estructura de objeto conocida en un bucle activo, puede obtener MUCHO MÁS RENDIMIENTO simplemente integrando el procedimiento de clonación y construyendo manualmente el objeto.
Los motores de rastreo de JavaScript son muy
for..in
malos para optimizar los bucles y comprobar hasOwnProperty también lo ralentizará. Clonación manual cuando la velocidad es una necesidad absoluta.Tenga cuidado al usar el
JSON.parse(JSON.stringify(obj))
método enDate
objetos:JSON.stringify(new Date())
devuelve una representación de cadena de la fecha en formato ISO, queJSON.parse()
no se convierte de nuevo en unDate
objeto. Vea esta respuesta para más detalles .Además, tenga en cuenta que, al menos en Chrome 65, la clonación nativa no es el camino a seguir. De acuerdo con JSPerf, realizar la clonación nativa mediante la creación de una nueva función es casi 800x más lento que usar JSON.stringify que es increíblemente rápido todo el camino a través del tablero.
Actualización para ES6
Si está utilizando Javascript ES6, pruebe este método nativo para la clonación o copia superficial.
fuente
keys
de suobject
, que tienenfunctions
como valores, porqueJSON
no admite funciones.JSON.parse(JSON.stringify(obj))
de Objetos de fecha también convertirá la fecha a UTC en la representación de cadena en el formato ISO8601 .Suponiendo que solo tiene variables y no funciones en su objeto, puede usar:
fuente
JSON
se implementa en el código nativo (en la mayoría de los navegadores), esto será considerablemente más rápido que el uso de cualquier otra solución de copia profunda basada en javascript, y a veces puede ser más rápido que una técnica de copia superficial basada en javascript (ver: jsperf.com/cloning -un objeto / 79 ).JSON.stringify({key: undefined}) //=> "{}"
Date
objetos que están almacenados dentro del objeto, convirtiéndolos en forma de cadena.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 integrados, pero además de los pocos tipos admitidos por JSON, también es compatible con Fechas, RegExps, Mapas, Conjuntos, Blobs, Listas de archivos, ImageDatas, Matrices dispersas, Matrices escritas, y probablemente más en el futuro . También conserva referencias dentro de los datos clonados, lo que le permite soportar estructuras cíclicas y recursivas que causarían errores para JSON.
Soporte en Node.js: Experimental 🙂
El
v8
módulo en Node.js actualmente (a partir del Nodo 11) expone la API de serialización estructurada directamente , pero esta funcionalidad todavía está marcada como "experimental" y está sujeta a cambios o eliminación en futuras versiones. Si está utilizando una versión compatible, clonar un objeto es tan simple como:Soporte directo en navegadores: ¿quizás eventualmente? 😐
Actualmente, los navegadores no proporcionan una interfaz directa para el algoritmo de clonación estructurada, pero
structuredClone()
se ha discutido una función global en whatwg / html # 793 en GitHub . Como se propone actualmente, usarlo para la mayoría de los propósitos sería tan simple como:A menos que se envíe esto, las implementaciones estructuradas de clones de los navegadores solo se exponen indirectamente.
Solución asíncrona: utilizable. 😕
La forma más económica de crear un clon estructurado con las API existentes es publicar los datos a través de un puerto de un MessageChannels . El otro puerto emitirá un
message
evento con un clon estructurado del adjunto.data
. Desafortunadamente, escuchar estos eventos es necesariamente asíncrono, y las alternativas sincrónicas son menos prácticas.Ejemplo de uso:
Soluciones alternativas sincrónicas: ¡horrible! 🤢
No hay buenas opciones para crear clones estructurados sincrónicamente. Aquí hay un par de trucos poco prácticos en su lugar.
history.pushState()
yhistory.replaceState()
ambos crean un clon estructurado de su primer argumento y le asignan ese valorhistory.state
. Puede usar esto para crear un clon estructurado de cualquier objeto como este:Ejemplo de uso:
Mostrar fragmento de código
Aunque sincrónico, esto puede ser extremadamente lento. Se incurre en todos los gastos generales asociados con la manipulación del historial del navegador. Llamar a este método repetidamente puede hacer que Chrome deje de responder temporalmente.
El
Notification
constructor crea un clon estructurado de sus datos asociados. También intenta mostrar una notificación del navegador al usuario, pero esto fallará silenciosamente a menos que haya solicitado permiso de notificación. En caso de que tenga el permiso para otros fines, cerraremos inmediatamente la notificación que hemos creado.Ejemplo de uso:
Mostrar fragmento de código
fuente
history.pushState()
y sehistory.replaceState()
configuran sincrónicamentehistory.state
en un clon estructurado de su primer argumento. Un poco raro, pero funciona. Estoy actualizando mi respuesta ahora.Si no hubiera ninguno incorporado, podría intentar:
fuente
La manera eficiente de clonar (no clonar en profundidad) un objeto en una línea de código
Un
Object.assign
método es parte del estándar ECMAScript 2015 (ES6) y hace exactamente lo que necesita.Lee mas...
El polyfill para admitir navegadores antiguos:
fuente
Código:
Prueba:
fuente
var obj = {}
yobj.a = obj
from.constructor
esDate
por ejemplo. ¿Cómo se alcanzará la terceraif
prueba cuando la segunda prueba tengaif
éxito y haga que la función regrese (desdeDate != Object && Date != Array
)?Esto es lo que estoy usando:
fuente
cloneObject({ name: null })
=>{"name":{}}
typeof null > "object"
peroObject.keys(null) > TypeError: Requested keys of a value that is not an object.
cambia la condición aif(typeof(obj[i])=="object" && obj[i]!=null)
Copia profunda por rendimiento: clasificado de mejor a peor
Copie en profundidad una matriz de cadenas o números (un nivel, sin punteros de referencia):
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 de Underscore.js; hará una copia profunda de los elementos de la matriz.
Donde la reasignación tiene el rendimiento más rápido:
Y .slice () tiene un mejor rendimiento que .concat (), http://jsperf.com/duplicate-array-slice-vs-concat/3
Copie en profundidad una matriz de objetos (dos o más niveles - punteros de referencia):
Escriba una función personalizada (tiene un rendimiento más rápido que $ .extend () o JSON.parse):
Use funciones de utilidad de terceros:
Donde $ .extend de jQuery tiene mejor rendimiento:
fuente
out[key] = (typeof v === "object" && v !== null) ? copy(v) : v;
fuente
Copia profunda de objetos en JavaScript (creo que el mejor y el más simple)
1. Usando JSON.parse (JSON.stringify (objeto));
2.Utilizando el método creado
3. Usando el enlace _.cloneDeep de Lo-Dash lodash
4. Usando el método Object.assign ()
PERO MAL CUANDO
5.Utilizando Underscore.js _.clone link Underscore.js
PERO MAL CUANDO
JSBEN.CH Performance Benchmarking Playground 1 ~ 3 http://jsben.ch/KVQLd
fuente
Object.assign()
no realiza una copia profundalet data = {title:["one", "two"]}; let tmp = cloneObject(data); tmp.title.pop();
arroja:TypeError: tmp.title.pop is not a function
(por supuesto, pop () funciona bien si solodo let tmp = data
; pero luego no puedo modificar tmp sin afectar los datos)Hay una biblioteca (llamada "clon") , que hace esto bastante bien. Proporciona la clonación / copia recursiva más completa de objetos arbitrarios que conozco. También admite referencias circulares, que aún no están cubiertas por las otras respuestas.
También puedes encontrarlo en npm . Se puede usar tanto para el navegador como para Node.js.
Aquí hay un ejemplo sobre cómo usarlo:
Instalarlo con
o empaquétalo con Ender .
También puede descargar el código fuente manualmente.
Entonces puedes usarlo en tu código fuente.
(Descargo de responsabilidad: soy el autor de la biblioteca).
fuente
JSON.parse(JSON.stringify(obj))
?Cloning
un objeto siempre fue una preocupación en JS, pero se trataba de antes de ES6, enumero diferentes formas de copiar un objeto en JavaScript a continuación, imagina que tienes el objeto a continuación y me gustaría tener una copia profunda de eso:Hay pocas formas de copiar este objeto, sin cambiar el origen:
1) ES5 +, utilizando una función simple para hacer la copia por usted:
2) ES5 +, utilizando JSON.parse y JSON.stringify.
3) AngularJs:
4) jQuery:
5) subrayado Js y Loadash:
Espero que esto ayude ...
fuente
Object.assign
no no copiar profunda. Ejemplo:var x = { a: { b: "c" } }; var y = Object.assign({}, x); x.a.b = "d"
. Si esto fuera una copia profunda, loy.a.b
seguiría siendoc
, pero es ahorad
.Sé que esta es una publicación antigua, pero pensé que podría ser de alguna ayuda para la próxima persona que tropieza.
Mientras no asignes un objeto a nada, no mantendrá ninguna referencia en la memoria. Por lo tanto, para crear un objeto que desee compartir entre otros objetos, deberá crear una fábrica como esta:
fuente
const defaultFoo = { a: { b: 123 } };
puedes irconst defaultFoo = () => ({ a: { b: 123 } };
y tu problema está resuelto. Sin embargo, realmente no es una respuesta a la pregunta. Podría haber tenido más sentido como un comentario sobre la pregunta, no como una respuesta completa.Si lo está utilizando, la biblioteca Underscore.js tiene un método de clonación .
fuente
.clone(...)
método de una biblioteca de utilidad . Todas las bibliotecas principales las tendrán, y las breves y repetidas respuestas no detalladas no son útiles para la mayoría de los visitantes, que no utilizarán esa biblioteca en particular.Aquí hay una versión de la respuesta anterior de ConroyP que funciona incluso si el constructor ha requerido parámetros:
Esta función también está disponible en mi biblioteca simpleoo .
Editar:
Aquí hay una versión más robusta (gracias a Justin McCandless, ahora también admite referencias cíclicas):
fuente
Lo siguiente crea dos instancias del mismo objeto. Lo encontré y lo estoy usando actualmente. Es simple y fácil de usar.
fuente
Crockford sugiere (y prefiero) usar esta función:
Es conciso, funciona como se esperaba y no necesita una biblioteca.
EDITAR:
Este es un polyfill para
Object.create
, así que también puedes usarlo.NOTA: Si usa algo de esto, puede tener problemas con alguna iteración de quién lo usa
hasOwnProperty
. Porque,create
crea un nuevo objeto vacío que heredaoldObject
. Pero sigue siendo útil y práctico para clonar objetos.Por ejemplo si
oldObject.a = 5;
pero:
fuente
var extendObj = function(childObj, parentObj) { var tmpObj = function () {} tmpObj.prototype = parentObj.prototype; childObj.prototype = new tmpObj(); childObj.prototype.constructor = childObj; };
... davidshariff.com/blog/javascript-inheritance-patternsLodash tiene un buen método _.cloneDeep (value) :
fuente
.clone(...)
método de una biblioteca de utilidad . Todas las bibliotecas principales las tendrán, y las breves y repetidas respuestas no detalladas no son útiles para la mayoría de los visitantes, que no utilizarán esa biblioteca en particular._.merge({}, objA)
. Si solo lodash no mutara objetos en primer lugar, entonces laclone
función no sería necesaria.fuente
Shallow copy one-liner ( ECMAScript 5th edition ):
Y copia superficial de una sola línea ( ECMAScript 6ta edición , 2015):
fuente
Object.keys
omite propiedades no enumerables y heredadas. Además, pierde los descriptores de propiedad al realizar una asignación directa.Solo porque no vi a AngularJS mencionado y pensé que la gente querría saber ...
angular.copy
También proporciona un método de copia profunda de objetos y matrices.fuente
angular.extend({},obj);
jQuery.extend
yangular.extend
son las dos copias de poca profundidad.angular.copy
Es una copia profunda.Parece que todavía no existe un operador de clonación profunda ideal para objetos con forma de matriz. Como ilustra el siguiente código, el clonador jQuery de John Resig convierte matrices con propiedades no numéricas en objetos que no son matrices, y el clonador JSON de RegDwight descarta las propiedades no numéricas. Las siguientes pruebas ilustran estos puntos en varios navegadores:
fuente
Tengo dos buenas respuestas dependiendo de si su objetivo es clonar un "objeto JavaScript simple" o no.
Supongamos también que su intención es crear un clon completo sin referencias prototipo al objeto de origen. Si no está interesado en un clon completo, puede usar muchas de las rutinas Object.clone () proporcionadas en algunas de las otras respuestas (patrón de Crockford).
Para los objetos JavaScript antiguos y simples, una manera buena y probada de clonar un objeto en tiempos de ejecución modernos es simplemente:
Tenga en cuenta que el objeto fuente debe ser un objeto JSON puro. Es decir, todas sus propiedades anidadas deben ser escalares (como boolean, string, array, object, etc.). Las funciones u objetos especiales como RegExp o Date no serán clonados.
¿Es eficiente? Diablos si. Hemos probado todo tipo de métodos de clonación y esto funciona mejor. Estoy seguro de que algunos ninjas podrían evocar un método más rápido. Pero sospecho que estamos hablando de ganancias marginales.
Este enfoque es simple y fácil de implementar. Envuélvalo en una función conveniente y si realmente necesita obtener algo de ganancia, continúe más adelante.
Ahora, para objetos JavaScript no simples, no hay una respuesta realmente simple. De hecho, no puede ser debido a la naturaleza dinámica de las funciones de JavaScript y el estado interno del objeto. La clonación profunda de una estructura JSON con funciones internas requiere que vuelva a crear esas funciones y su contexto interno. Y JavaScript simplemente no tiene una forma estandarizada de hacerlo.
La forma correcta de hacer esto, una vez más, es a través de un método conveniente que declare y reutilice dentro de su código. El método de conveniencia puede dotarse de cierta comprensión de sus propios objetos para que pueda asegurarse de recrear correctamente el gráfico dentro del nuevo objeto.
Estamos escritos por nuestra cuenta, pero el mejor enfoque general que he visto está cubierto aquí:
http://davidwalsh.name/javascript-clone
Esta es la idea correcta. El autor (David Walsh) ha comentado la clonación de funciones generalizadas. Esto es algo que puede elegir hacer, dependiendo de su caso de uso.
La idea principal es que necesita manejar especialmente la creación de instancias de sus funciones (o clases de prototipos, por así decirlo) por tipo. Aquí, ha proporcionado algunos ejemplos para RegExp y Date.
Este código no solo es breve, sino que también es muy legible. Es bastante fácil de extender.
¿Es esto eficiente? Diablos si. Dado que el objetivo es producir un verdadero clon de copia profunda, tendrá que recorrer los miembros del gráfico del objeto fuente. Con este enfoque, puede ajustar exactamente qué miembros secundarios tratar y cómo manejar manualmente los tipos personalizados.
Ahí vas. Dos enfoques. Ambos son eficientes en mi opinión.
fuente
Esta no es generalmente la solución más eficiente, pero hace lo que necesito. Casos de prueba simples a continuación ...
Prueba de matriz cíclica ...
Prueba de funcionamiento...
fuente
AngularJS
Bueno, si estás usando angular, podrías hacer esto también
fuente
No estoy de acuerdo con la respuesta con los mejores votos aquí . Un clon profundo recursivo es mucho más rápido que el enfoque JSON.parse (JSON.stringify (obj)) mencionado.
Y aquí está la función para referencia rápida:
fuente
if(o instanceof Date) return new Date(o.valueOf());
después de verificar nulo `fuente
Solo cuando puede usar ECMAScript 6 o transpilers .
caracteristicas:
Código:
fuente
Aquí hay un método completo de clone () que puede clonar cualquier objeto JavaScript. Maneja casi todos los casos:
fuente