¿Cuál es la forma más eficiente de clonar en profundidad un objeto en JavaScript?
Frecuentes
Visto 2,190 veces
5176
¿Cuál es la forma más eficaz de clonar un objeto JavaScript? He visto obj = eval(uneval(o));
siendo utilizado, pero eso no es estándar y solo es compatible con Firefox.
He hecho cosas como obj = JSON.parse(JSON.stringify(o));
pero cuestiona la eficiencia.
También he visto funciones de copia recursiva con varios defectos.
Me sorprende que no exista una solución canónica.
30 Respuestas
5024
Clonación profunda nativa
Se llama "clonación estructurada", funciona de forma experimental en el Nodo 11 y posteriores, y es de esperar que llegue a los navegadores. Ver esta respuesta para más información.
Clonación rápida con pérdida de datos: JSON.parse / stringify
Si no usas Date
s, funciones, undefined
, Infinity
, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, Sparse Arrays, Typed Arrays u otros tipos complejos dentro de su objeto, un trazador de líneas muy simple para clonar en profundidad un objeto es:
JSON.parse(JSON.stringify(object))
const a = {
string: 'string',
number: 123,
bool: false,
nul: null,
date: new Date(), // stringified
undef: undefined, // lost
inf: Infinity, // forced to 'null'
re: /.*/, // lost
}
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()
Ver La respuesta de Corban para los 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 reinventes la rueda - si ya está usando una biblioteca, verifique si tiene una función de clonación de objetos. Por ejemplo,
- lodash -
cloneDeep
; se puede importar por separado a través del lodash.clondeep módulo y es probablemente su mejor opción si aún no está utilizando una biblioteca que proporciona una función de clonación profunda - AngularJS -
angular.copy
- jQuery-
jQuery.extend(true, { }, oldObject)
;.clone()
solo clona elementos DOM
ES6 (superficial Copiar)
Para completar, tenga en cuenta que ES6 ofrece dos mecanismos de copia superficial: Object.assign()
y el sintaxis extendida. que copia valores de todas las propiedades propias enumerables de un objeto a otro. Por ejemplo:
var A1 = {a: "2"};
var A2 = Object.assign({}, A1);
var A3 = {...A1}; // Spread Syntax
Respondido el 26 de enero de 21 a las 20:01
¡Cuidado! var A = { b: [ { a: [ 1, 2, 3], b: [4, 5, 6], c: [7, 8, 9] } ] }; B = Object.assign( {}, A ); delete B.b[0].b;
¡También modificará el objeto A! - Gabriel Hautclocq
@GabrielHautclocq ¿cuál es la solución en este caso? - MedBouzid
@GabrielHautclocq Acaba de descubrir lo que significa una "copia superficial". "@medBouzid" La solución es hacer una "copia profunda" del objeto, en lugar de una copia superficial. - victoria
@Gabriel Hautclocq esto es porque A.b
or B.b
ambos se refieren al mismo objeto en la memoria. Si A
tenía una propiedad con un valor que no era un objeto (como números o cadenas), se copiará normalmente. Pero cuando se copia una propiedad que contiene un valor de objeto, se copia por referencia, no por valor. Además, tenga en cuenta que una matriz es un objeto en JS. prueba: typeof [] == 'object' && [] instanceof Array
- Unicornista
@Unicornist Sí y es por eso que Object.assign no responde a la pregunta: "¿Cuál es la forma más eficiente de clonar en profundidad un objeto en JavaScript?". Por lo tanto, al menos NO debe presentarse como una solución ES6 para la clonación profunda. El título "ES6" es engañoso, al menos debería cambiarse para reflejar que este no es un método de clonación profunda. La palabra "superficial" es fácil de pasar por alto y muchas personas simplemente toman la solución más simple que encuentran en Stack Overflow sin leerlo todo. Es peligroso confiar en Object.assign para la clonación de objetos. De ahí mi observación. - Gabriel Hautclocq
2335
Consulte este punto de referencia: http://jsben.ch/#/bWfk9
En mis pruebas anteriores, donde la velocidad era una preocupación principal, encontré
JSON.parse(JSON.stringify(obj))
para ser la forma más lenta de clonar en profundidad un objeto (es más lento que jQuery.extender con deep
bandera establecida como verdadera en un 10-20%).
jQuery.extend es bastante rápido cuando el deep
la bandera está configurada en false
(clon superficial). Es una buena opción, porque incluye algo de lógica adicional para la validación de tipos y no copia propiedades no definidas, etc., pero esto también lo ralentizará un poco.
Si conoce la estructura de los objetos que está intentando clonar o puede evitar matrices anidadas profundas, puede escribir un sencillo for (var i in obj)
bucle 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 alineando el procedimiento de clonación y construyendo manualmente el objeto.
Los motores de rastreo de JavaScript apestan a la hora de optimizar for..in
bucles y comprobar hasOwnProperty también lo ralentizará. Clon manual cuando la velocidad es una necesidad absoluta.
var clonedObject = {
knownProp: obj.knownProp,
..
}
Tenga cuidado con el JSON.parse(JSON.stringify(obj))
método en Date
objetos - JSON.stringify(new Date())
devuelve una representación de cadena de la fecha en formato ISO, que JSON.parse()
no se convertir de nuevo en un Date
objeto. Consulte esta respuesta para obtener más detalles..
Además, tenga en cuenta que, al menos en Chrome 65, la clonación nativa no es el camino a seguir. Según 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 en todos los ámbitos.
Si está utilizando Javascript ES6, pruebe este método nativo para la clonación o copia superficial.
Object.assign({}, obj);
respondido 20 nov., 19:04
Tenga en cuenta que hay 2 errores en su banco: primero, compara algunas clonaciones superficiales (lodash _.clone
y Object.assign
) a una clonación profunda (JSON.parse(JSON.stringify())
). En segundo lugar, dice "clon profundo" para lodash, pero en su lugar hace un clon superficial. - papillon
502
Suponiendo que solo tiene variables y no funciones en su objeto, puede usar:
var newObject = JSON.parse(JSON.stringify(oldObject));
Respondido 24 Oct 14, 00:10
442
Clonación estructurada
El estándar HTML incluye un algoritmo de serialización / clonación estructurada interna que pueden 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 admite fechas, expresiones regulares, mapas, conjuntos, blobs, listas de archivos, datos de imágenes, matrices dispersas, matrices con tipo y probablemente más en el futuro. . También conserva las referencias dentro de los datos clonados, lo que le permite admitir 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 versiones futuras. Si está usando una versión compatible, clonar un objeto es tan simple como:
const v8 = require('v8');
const structuredClone = obj => {
return v8.deserialize(v8.serialize(obj));
};
Soporte directo en navegadores: ¿Quizás eventualmente? 😐
Actualmente, los navegadores no proporcionan una interfaz directa para el algoritmo de clonación estructurada, sino una structuredClone()
La función se ha discutido en whatwg / html # 793 en GitHub. Como se propone actualmente, usarlo para la mayoría de los propósitos sería tan simple como:
const clone = structuredClone(original);
A menos que se envíe, las implementaciones de clones estructurados de los navegadores solo se exponen indirectamente.
Solución alternativa asincrónica: utilizable. 😕
La forma más económica de crear un clon estructurado con API existentes es publicar los datos a través de un puerto de un MensajeCanales. El otro puerto emitirá un message
evento con un clon estructurado del adjunto .data
. Desafortunadamente, escuchar estos eventos es necesariamente asincrónico y las alternativas sincrónicas son menos prácticas.
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);
Ejemplo de uso:
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();
Soluciones sincrónicas: ¡Horrible! 🤢
No hay buenas opciones para crear clones estructurados de forma sincrónica. En su lugar, aquí hay un par de trucos poco prácticos.
history.pushState()
y history.replaceState()
ambos crean un clon estructurado de su primer argumento y asignan ese valor a history.state
. Puede usar esto para crear un clon estructurado de cualquier objeto como este:
const structuredClone = obj => {
const oldState = history.state;
history.replaceState(obj, null);
const clonedObj = history.state;
history.replaceState(oldState, null);
return clonedObj;
};
Ejemplo de uso:
'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();
Aunque sincrónico, esto puede ser extremadamente lento. 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.
const structuredClone = obj => {
const n = new Notification('', {data: obj, silent: true});
n.onshow = n.close.bind(n);
return n.data;
};
Ejemplo de uso:
'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 n = new Notification('', {data: obj, silent: true});
n.close();
return n.data;
};
main();
Respondido 05 Feb 19, 02:02
¡Esto está tan mal! Esa API no está diseñada para usarse de esta manera. - Fardín K.
Como el tipo que implementó pushState en Firefox, siento una extraña mezcla de orgullo y repulsión por este truco. Bien hecho muchachos. - Justin l.
El truco pushState o Notification no funciona para algunos tipos de objetos como Function - Shishir Arora
@ShishirArora Tienes razón, lo acabo de intentar, arroja una 'DOMException no detectada: el objeto no se pudo clonar'. Esto también es válido para el truco de notificaciones. - ADJenks
338
Si no hubiera uno incorporado, podría 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;
}
Respondido el 27 de Septiembre de 18 a las 09:09
156
La forma eficiente de clonar (no clonar en profundidad) un objeto en una línea de código
An Object.assign
El método 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 utiliza para copiar los valores de todas las propiedades propias enumerables de uno o más objetos de origen a un objeto de destino.
EL polyfill para admitir 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;
}
});
}
Respondido el 20 de junio de 20 a las 10:06
Esto no se copia de forma recursiva, por lo que realmente no ofrece una solución al problema de clonar un objeto. - mblanco
Este método funcionó, aunque probé algunos y _.extend ({}, (obj)) fue POR LEJOS el más rápido: 20 veces más rápido que JSON.parse y 60% más rápido que Object.assign, por ejemplo. Copia todos los subobjetos bastante bien. - Nico
@mwhite hay una diferencia entre clonar y clonar en profundidad. De hecho, esta respuesta se clona, pero no se clona en profundidad. - Meirion Hughes
105
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);
respondido 26 mar '11, 14:03
104
Esto es lo que estoy 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;
}
Respondido 16 Jul 13, 17:07
83
Copia profunda por rendimiento: Clasificado de mejor a peor
- Reasignación "=" (matrices de cadenas, matrices de números, solo)
- Slice (matrices de cadenas, matrices de números, solo)
- Concatenación (matrices de cadenas, matrices de números, solo)
- Función personalizada: copia recursiva o for-loop
- $ .extend de jQuery
- JSON.parse (matrices de cadenas, matrices de números, matrices de objetos, solo)
- Underscore.js's _.clone (matrices de cadenas, matrices de números, solo)
- _.CloneDeep de Lo-Dash
Copia 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:
var arr1 = ['a', 'b', 'c'];
var arr2 = arr1;
arr1 = ['a', 'b', 'c'];
Y .slice () tiene un 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
Realice una copia profunda de una matriz de objetos (dos o más niveles, punteros de referencia):
var arr1 = [{object:'a'}, {object:'b'}];
Escriba una función personalizada (tiene un rendimiento más rápido 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 $ .extend de jQuery tiene un mejor rendimiento:
respondido 20 mar '18, 15:03
78
Copiar objetos en profundidad en JavaScript (creo que el mejor y el más simple)
1. Usando JSON.parse (JSON.stringify (objeto));
var obj = {
a: 1,
b: {
c: 2
}
}
var newObj = JSON.parse(JSON.stringify(obj));
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } }
2.Usando 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.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } }
3. Usando _.cloneDeep de Lo-Dash aquí lodash
var obj = {
a: 1,
b: {
c: 2
}
}
var newObj = _.cloneDeep(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } }
4. Usando el método 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 INCORRECTO CUANDO
var obj = {
a: 1,
b: {
c: 2
}
}
var newObj = Object.assign({}, obj);
obj.b.c = 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.Uso de Underscore.js _.clone aquí 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 INCORRECTO CUANDO
var obj = {
a: 1,
b: {
c: 2
}
}
var newObj = _.cloneDeep(obj);
obj.b.c = 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.)
JSBEN.CH Patio de juegos de evaluación comparativa de rendimiento 1 ~ 3 http://jsben.ch/KVQLd
respondido 20 nov., 19:04
Oye, tu último ejemplo está mal. En mi opinión, debe usar _clone y no _cloneDeep para el ejemplo incorrecto. - kenanyildiz90
Este método creado (2.) no funcionará para matrices, ¿verdad? - Toivo Sawén
65
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});
Respondido 21 Feb 13, 15:02
60
Hay un biblioteca (llamada "clon"), eso 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.
solicite encuéntralo en npm, también. Se puede utilizar tanto para el navegador como para Node.js.
Aquí hay un ejemplo de cómo usarlo:
Instalarlo con
npm install clone
o empaquetarlo 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).
respondido 27 mar '13, 08:03
59
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 te gustaría tener una copia profunda de eso:
var obj = {a:1, b:2, c:3, d:4};
Hay algunas formas de copiar este objeto, sin cambiar el origen:
1) ES5 +, usando una función simple para hacer la copia por usted:
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 +, usando 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) UnderscoreJs y Loadash:
var deepCopyObj = _.cloneDeep(obj); //latest version UndescoreJs makes shallow copy
Espero que estos ayuden ...
Respondido 24 Feb 18, 17:02
54
Sé que esta es una publicación antigua, pero pensé que podría ser de alguna ayuda para la próxima persona que se tropiece.
Mientras no asigne un objeto a nada, no mantiene ninguna referencia en la memoria. Entonces, para crear un objeto que desee compartir entre otros objetos, tendrá que crear una fábrica como esta:
var a = function(){
return {
father:'zacharias'
};
},
b = a(),
c = a();
c.father = 'johndoe';
alert(b.father);
respondido 17 mar '15, 01:03
48
Si lo está utilizando, el Underscore.js la biblioteca tiene un clonar método.
var newObject = _.clone(oldObject);
Respondido 22 Jul 16, 18:07
42
Aquí hay una versión de la respuesta de ConroyP anterior que funciona incluso si el constructor tiene parámetros requeridos:
//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;
}
Esta función también está disponible en mi simpleoo biblioteca.
Edit:
Aquí hay una versión más robusta (gracias a Justin McCandless, ahora también admite referencias cíclicas):
/**
* 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();
};
}
respondido 12 mar '17, 18:03
30
Lo siguiente crea dos instancias del mismo objeto. Lo encontré y lo estoy usando actualmente. Es simple y fácil de usar.
var objToCreate = JSON.parse(JSON.stringify(cloneThis));
Respondido el 01 de diciembre de 16 a las 16:12
25
Crockford sugiere (y prefiero) usar esta función:
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
var newObject = object(oldObject);
Es conciso, funciona como se esperaba y no necesita una biblioteca.
EDIT:
Este es un polyfill para Object.create
, por lo que también puede usar esto.
var newObject = Object.create(oldObject);
NOTA: Si usa algo de esto, puede tener problemas con algunas iteraciones que usan 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
pero:
oldObject.hasOwnProperty(a); // is true
newObject.hasOwnProperty(a); // is false
respondido 12 mar '16, 14:03
23
Lodash tiene un buen _.cloneDeep (valor) método:
var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
Respondido el 31 de diciembre de 17 a las 12:12
21
function clone(obj)
{ var clone = {};
clone.prototype = obj.prototype;
for (property in obj) clone[property] = obj[property];
return clone;
}
Respondido el 23 de Septiembre de 08 a las 17:09
21
Copia superficial de una sola línea (ECMAScript 5ta 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
Y copia superficial de una sola línea (ECMAScript 6ta 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
Respondido 22 Jul 16, 18:07
18
Parece que todavía no hay un operador de clonación profunda ideal para objetos tipo matriz. Como ilustra el código siguiente, 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 elimina las propiedades no numéricas. Las siguientes pruebas ilustran estos puntos en varios navegadores:
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)
Respondido el 13 de diciembre de 13 a las 19:12
17
Solo porque no vi AngularJS mencionado y pensó que la gente podría querer saber ...
angular.copy
también proporciona un método de copia profunda de objetos y matrices.
Respondido 15 Oct 16, 19:10
o podría usarse de la misma manera que jQiery extend: angular.extend({},obj);
- Galvaní
@Galvani: Cabe señalar que jQuery.extend
y angular.extend
son copias superficiales. angular.copy
es una copia profunda. - Dan Atkinson
16
Tengo dos buenas respuestas dependiendo de si su objetivo es clonar un "objeto JavaScript simple y antiguo" o no.
Supongamos también que su intención es crear un clon completo sin referencias de 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 objetos de JavaScript antiguos y sencillos, una buena manera probada y verdadera de clonar un objeto en tiempos de ejecución modernos es bastante simple:
var clone = JSON.parse(JSON.stringify(obj));
Tenga en cuenta que el objeto de origen debe ser un objeto JSON puro. Es decir, todas sus propiedades anidadas deben ser escalares (como booleano, cadena, matriz, objeto, etc.). Las funciones u objetos especiales como RegExp o Date no se clonarán.
¿Es eficiente? Diablos, sí. Hemos probado todo tipo de métodos de clonación y esto funciona mejor. Estoy seguro de que algún ninja podría inventar 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 de conveniencia y si realmente necesita exprimir algo de ganancia, hágalo más adelante.
Ahora bien, para los objetos JavaScript que no son simples, no hay una respuesta realmente simple. De hecho, no puede haberlo debido a la naturaleza dinámica de las funciones de JavaScript y el estado del objeto interno. La clonación profunda de una estructura JSON con funciones en su interior 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 de conveniencia 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 nosotros mismos, pero el mejor enfoque general que he visto está cubierto aquí:
http://davidwalsh.name/javascript-clone
Ésta es la idea correcta. El autor (David Walsh) ha comentado la clonación de funciones generalizadas. Esto es algo que puede optar por hacer, según su caso de uso.
La idea principal es que necesita un manejo especial de la instanciación de sus funciones (o clases prototípicas, 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 ampliar.
¿Es esto eficiente? Diablos, sí. Dado que el objetivo es producir un verdadero clon de copia profunda, tendrá que recorrer los miembros del gráfico del objeto de origen. 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.
Respondido 22 Jul 16, 18:07
14
Por lo general, esta no es la solución más eficiente, pero hace lo que necesito. Casos de prueba simples a continuación ...
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;
}
Prueba de matriz cíclica ...
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
Prueba de funcionamiento...
f = new Function
f.a = a
ff = clone(f)
ff === f //=> true
ff.a === a //=> false
Respondido 03 Abr '11, 03:04
12
No estoy de acuerdo con la respuesta con más votos. aquí. Una Clon profundo recursivo is mucho mas rápido que la JSON.parse (JSON.stringify (obj)) enfoque mencionado.
- Jsperf lo clasifica como el número uno aquí: https://jsperf.com/deep-copy-vs-json-stringify-json-parse/5
- jsben de la respuesta anterior actualizada para mostrar que un clon profundo recursivo supera a todos los demás mencionados: http://jsben.ch/13YKQ
Y aquí está la función para referencia rápida:
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
}
Respondido el 18 de junio de 17 a las 07:06
Me gustó este enfoque, pero no maneja las fechas correctamente; considere agregar algo como if(o instanceof Date) return new Date(o.valueOf());
después de verificar nulo '- Luis
Se bloquea en referencias circulares. - Harry
En el último Firefox estable, esto es mucho más largo que las otras estrategias en ese enlace Jsben.ch, en un orden de magnitud o más. Golpea a los demás en la dirección equivocada. - WBT
12
// 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;
};
Respondido el 17 de enero de 18 a las 16:01
12
Para las personas que quieren usar el JSON.parse(JSON.stringify(obj))
versión, pero sin perder los objetos de fecha, puede utilizar la segundo argumento de parse
Método para convertir las cadenas de nuevo a la fecha:
function clone(obj) {
var regExp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
return JSON.parse(JSON.stringify(obj), function(k, v) {
if (typeof v === 'string' && regExp.test(v))
return new Date(v)
return v;
})
}
// usage:
var original = {
a: [1, null, undefined, 0, {a:null}, new Date()],
b: {
c(){ return 0 }
}
}
var cloned = clone(original)
console.log(cloned)
Respondido 24 Oct 20, 14:10
No es un clon del 100%. vsync
11
Aquí hay un método clone () completo que puede clonar cualquier objeto JavaScript. Maneja casi todos los casos:
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;
};
Respondido 22 Jul 16, 18:07
Convierte primitivas en objetos envoltorios, lo que no es una buena solución en la mayoría de los casos. - Marinero danubiano
@DanubianSailor: no creo que lo haga ... parece devolver primitivas desde el principio, y no parece estar haciendo nada que las convierta en objetos de envoltura a medida que se devuelven. - jimbo jonny
11
Solo cuando puedas usar ECMAScript 6 or transpiladores.
Características:
- No activará getter / setter mientras copia.
- Conserva getter / setter.
- Conserva la información del prototipo.
- Funciona con ambos objeto-literal y funcional OO estilos de escritura.
Código:
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;
}
Respondido 22 Jul 16, 18:07
No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas javascript object clone or haz tu propia pregunta.
Eval no es malvado. Usar eval mal es. Si tiene miedo de sus efectos secundarios, lo está usando mal. Los efectos secundarios que temes son las razones para usarlo. Por cierto, ¿alguien respondió realmente a tu pregunta? - Heavy Gray
La clonación de objetos es un asunto complicado, especialmente con objetos personalizados de colecciones arbitrarias. Probablemente por eso no existe una forma inmediata de hacerlo. - b01
eval()
es generalmente una mala idea porque muchos optimizadores del motor Javascript tienen que apagarse cuando se trata de variables que se establecen a través deeval
. Solo teniendoeval()
en su código puede conducir a un peor rendimiento. - user56reinstatemonica8Posible duplicado de La forma más elegante de clonar un objeto JavaScript - John Slegers
Tenga en cuenta que
JSON
El 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}
- oriadam