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

¿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.

preguntado el 23 de septiembre de 08 a las 14:09

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? -

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. -

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 de eval. Solo teniendo eval() en su código puede conducir a un peor rendimiento. -

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

30 Respuestas

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 Dates, 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()

Ven 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,

ES6 (superficial Copiar)

Para completar, tenga en cuenta que ES6 ofrece dos mecanismos de copia superficial: Object.assign() y 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

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.

Actualización para ES6

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

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

Los objetos tienen propiedades, no variables. ;-) - robar

funciones y fechas también - vsync

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 sistema 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 sistema 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

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

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.

Leer más...

El sistema 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

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

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

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

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 Rendimiento Copia profunda de objetos en JavaScript

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

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

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.

Puede 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

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

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

Si lo está utilizando, el Underscore.js la biblioteca tiene un clonar método.

var newObject = _.clone(oldObject);

Respondido 22 Jul 16, 18:07

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

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

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

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

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

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

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

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

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

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

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.

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

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

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

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

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 or haz tu propia pregunta.