¿Cuál es la forma más eficaz de realizar una clonación profunda de un objeto en JavaScript?

StackOverflow https://stackoverflow.com/questions/122102

  •  02-07-2019
  •  | 
  •  

Pregunta

¿Cuál es la forma más eficaz de clonar un objeto JavaScript?He visto obj = eval(uneval(o)); siendo usado, 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 recursivas con varios defectos.
Me sorprende que no exista una solución canónica.

¿Fue útil?

Solución

Nota junio 2019: Originalmente, esta fue una respuesta a otra respuesta, no una respuesta adecuada a esta pregunta.No tengo idea de por qué fue elegida como la respuesta correcta.Pero dado que los votos a favor aumentaron y es, con diferencia, la respuesta número uno a esta pregunta, resumirá las soluciones como una respuesta wiki.

Clonación profunda nativa

Se llama "clonación estructurada", funciona experimentalmente en el Nodo 11 y posteriores y, con suerte, llegará a los navegadores.Ver esta respuesta para más detalles.

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, una línea muy simple para clonar profundamente 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 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ás usando una biblioteca, verifica si tiene una función de clonación de objetos.Por ejemplo,

ES6

Para completar, tenga en cuenta que ES6 ofrece dos mecanismos de copia superficial: Object.assign() y el operador de propagación.

Otros consejos

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

ser la forma más lenta de clonar profundamente un objeto (es más lento que jQuery.extender con deep indicador establecido como verdadero en un 10-20%).

jQuery.extend es bastante rápido cuando el deep la bandera está establecida 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 simple 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 integrando el procedimiento de clonación y construyendo manualmente el objeto.

Los motores de seguimiento de JavaScript apestan a la hora de optimizar for..in Los bucles y comprobar hasOwnProperty también te ralentizarán.Clonación manual cuando la velocidad es imprescindible.

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

Cuidado con el uso del 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 convertir de nuevo a un Date 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 a este JSPerf, realizar una 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 clonación o copia superficial.

Object.assign({}, obj);

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

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

Clonación estructurada

El estándar HTML incluye un algoritmo interno estructurado de clonación/serialización 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 admite fechas, expresiones regulares, mapas, conjuntos, blobs, listas de archivos, datos de imágenes, matrices dispersas, matrices tipificadas y probablemente más en el futuro. .También conserva referencias dentro de los datos clonados, lo que le permite admitir estructuras cíclicas y recursivas que causarían errores en 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 aún está marcada como "experimental" y sujeta a cambios o eliminación en futuras versiones.Si estás 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 interfaz global. structuredClone() La función ha sido discutida en whatwg/html#793 en GitHub.Tal 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 esto se envíe, las implementaciones de clones estructurados de los navegadores solo se exponen indirectamente.

Solución asincrónica:Usable.😕

La forma más sencilla de crear un clon estructurado con API existentes es publicar los datos a través de un puerto de un Canales de mensajes.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);

Uso de ejemplo:

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 existen buenas opciones para crear clones estructurados de forma sincrónica.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.Puedes 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;
};

Uso de ejemplo:

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

Uso de ejemplo:

'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();

Si no hubiera ninguno integrado, puedes intentar:

function clone(obj) {
    if (obj === null || typeof (obj) !== 'object' || 'isActiveClone' in obj)
        return obj;

    if (obj instanceof Date)
        var temp = new obj.constructor(); //or new Date(obj);
    else
        var temp = obj.constructor();

    for (var key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
            obj['isActiveClone'] = null;
            temp[key] = clone(obj[key]);
            delete obj['isActiveClone'];
        }
    }
    return temp;
}

La forma eficiente de clonar (no clonar profundamente) un objeto en una línea de código

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

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

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

Copia profunda por rendimiento:Clasificados de mejor a peor

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

Copie en profundidad una serie 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 mejor rendimiento que .concat(),http://jsperf.com/duplicate-array-slice-vs-concat/3

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

Copie en profundidad 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 mejor rendimiento:

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

Hay una biblioteca (llamada “clon”), eso lo hace bastante bien.Proporciona la clonación/copia recursiva más completa de objetos arbitrarios que yo sepa.También admite referencias circulares, que aún no están cubiertas por las otras respuestas.

Puede encuéntrelo 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 puedes 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.)

Sé que esta es una publicación antigua, pero pensé que podría ser de alguna ayuda para la próxima persona que tropiece.

Mientras no asigne un objeto a nada, no mantendrá ninguna referencia en la memoria.Entonces, para crear un objeto que quieras compartir con otros objetos, tendrás que crear una fábrica como esta:

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

Cloning un Objeto siempre fue una preocupación en JS, pero se trataba de antes de ES6. A continuación enumero diferentes formas de copiar un objeto en JavaScript, imagina que tienes el Objeto a continuación y te gustaría tener una copia profunda de él:

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) AngularJ:

var  deepCopyObj = angular.copy(obj);

4) jQuery:

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

5) SubrayadoJs y Loadash:

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

Espero que estos ayuden...

Si lo estás usando, el Subrayado.js la biblioteca tiene un clon método.

var newObject = _.clone(oldObject);

Copia profunda de objetos en JavaScript (creo que es lo mejor y lo 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 enlace 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 MAL 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.Usando Underscore.js _.clone enlace Subrayado.js

var obj = { 
  a: 1,
  b: 2
}

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

PERO MAL CUANDO

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = _.cloneDeep(obj);
obj.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.)

Medio de referencia.com

JSBEN.CH Zona de juegos de evaluación comparativa de rendimiento 1 ~ 3 http://jsben.ch/KVQLd Performance Deep copying objects in JavaScript

Aquí hay una versión de la respuesta anterior de ConroyP que funciona incluso si el constructor tiene los 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.

Editar:

Aquí hay una versión más sólida (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();
    };
}

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

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

var objects = [{ 'a': 1 }, { 'b': 2 }];

var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
function clone(obj)
 { var clone = {};
   clone.prototype = obj.prototype;
   for (property in obj) clone[property] = obj[property];
   return clone;
 }

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.


EDITAR:

Este es un polirelleno para Object.create, entonces también puedes usar esto.

var newObject = Object.create(oldObject);

NOTA: Si usa algo de esto, puede tener problemas con algunas iteraciones que usan hasOwnProperty.Porque, create crear un nuevo objeto vacío que herede 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

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

Sólo porque no vi angularjs Mencioné y pensé que la gente podría querer saber...

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

Parece que todavía no existe un operador de clonación profunda ideal para objetos similares a matrices.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 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)

Tengo dos buenas respuestas dependiendo de si su objetivo es clonar un "objeto JavaScript antiguo" o no.

Supongamos también que su intención es crear un clon completo sin referencias de prototipo al objeto fuente.Si no está interesado en un clon completo, puede utilizar muchas de las rutinas Object.clone() proporcionadas en algunas de las otras respuestas (patrón de Crockford).

Para objetos JavaScript antiguos y simples, 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 booleanas, cadenas, matrices, objetos, etc.).No se clonarán funciones u objetos especiales como RegExp o Date.

¿Es eficiente?Diablos, sí.Hemos probado todo tipo de métodos de clonación y este funciona mejor.Estoy seguro de que algún ninja podría idear 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 obtener algo de ganancia, hágalo más adelante.

Ahora bien, para los objetos JavaScript no simples, no existe una respuesta realmente simple.De hecho, no puede haberlo debido a la naturaleza dinámica de las funciones de JavaScript y al estado interno del objeto.La clonación profunda de una estructura JSON con funciones internas requiere recrear 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 mediante un método conveniente que declaras y reutilizas dentro de tu código.El método de conveniencia puede estar dotado 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 se trata 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 creación de instancias de sus funciones (o clases prototipo, por así decirlo) por tipo.Aquí, proporciona algunos ejemplos de RegExp y Date.

Este código no sólo es breve, sino que también es muy legible.Es bastante fácil de extender.

¿Es esto eficiente?Diablos, sí.Dado que el objetivo es producir un verdadero clon de copia profunda, tendrás que recorrer los miembros del gráfico del objeto fuente.Con este enfoque, puede modificar exactamente qué miembros secundarios tratar y cómo manejar manualmente los tipos personalizados.

Ahí vas.Dos enfoques.Ambos son eficientes en mi opinión.

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

angularjs

Bueno, si estás usando angular, también puedes hacer esto.

var newObject = angular.copy(oldObject);

No estoy de acuerdo con la respuesta con mayor número de votos. aquí.A Clon profundo recursivo es 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
}
// 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;
};

Para las personas que quieran utilizar el JSON.parse(JSON.stringify(obj)) versión, pero sin perder los objetos Date, puedes usar el segundo argumento de parse método para convertir las cadenas nuevamente a 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(x), function(k, v) {
    if (typeof v === 'string' && regExp.test(v))
      return new Date(v);
    return v;
  });
}

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;
};
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top