4 formas de clonar objetos en JavaScript

Dado que los objetos de JavaScript son tipos de referencia, no puede simplemente usar el operador igual (= ) para copiar un objeto. Cuando crea un objeto en JavaScript, el valor es no directorio asignado a la variable. En cambio, la variable solo contiene una referencia al valor.

¿Qué es el tipo de referencia?

Veamos el siguiente ejemplo para entender qué referencia tipo significa:

const obj1 = { mango: '🥭️', apple: '🍎' };

const obj2 = obj1;

console.log(
    obj1, // { mango: '🥭️', apple: '🍎' }
    obj2  // { mango: '🥭️', apple: '🍎' }
);

Como puede ver arriba, creé un objeto y luego lo asigné a una nueva variable usando el = operador. Ambos objetos generan los mismos pares clave-valor. ¡Hasta ahora todo bien!

Agreguemos ahora una nueva clave al primer objeto para ver qué sucede:

obj1.lemon = '🍋';

console.log(
    obj1, // { mango: '🥭️', apple: '🍎', lemon: '🍋' } ✅
    obj2  // { mango: '🥭️', apple: '🍎', lemon: '🍋' } ❌
);

Puedes ver que solo hice cambios en obj1 pero ha afectado a obj2 también. Esto no es lo que esperamos cuando copiamos un objeto. Sucede porque los objetos son tipos de referencia y cuando usamos = , solo copia el puntero a la memoria asignada al objeto y no al valor real.

Clon superficial frente a clon profundo

Un clon superficial solo copia tipos primitivos como cadenas, números y booleanos disponibles en el objeto. Cualquier objeto anidado o matriz no se copiará recursivamente. En su lugar, solo se copia una referencia al objeto en el nuevo objeto. Significa que tanto el objeto original como el objeto copiado siguen haciendo referencia al mismo objeto anidado.

Si el objeto original hace referencia a otros objetos externos, tampoco se copian recursivamente al crear una copia superficial del objeto. Solo se copian las referencias a los objetos externos.

Por otro lado, un clon profundo copia recursivamente todo:tipos de datos primitivos, objetos anidados y externos, matrices, funciones, fechas, etc. El objeto clonado es completamente independiente del objeto original.

JavaScript ofrece muchas formas de crear clones superficiales y profundos de objetos. Puede utilizar el operador de propagación (... ) y Object.assign() para crear rápidamente un duplicado de objeto poco profundo. Para la clonación profunda de objetos, puede escribir su propia función personalizada o usar una biblioteca de terceros como Lodash.

Object.assign() Método

La forma más sencilla y rápida de crear una copia superficial de un objeto es mediante el Object.assign(target, source1, soure2, ...) de ES6. método. Este método copia todas las propiedades enumerables propias de uno o más objetos de origen en un objeto de destino y devuelve el objeto de destino:

const fruits = { mango: '🥭️', apple: '🍎' };

const moreFruits = Object.assign({}, fruits);

console.log(moreFruits);
// { mango: '🥭️', apple: '🍎' }

Observe el {} vacío objeto de origen como primer parámetro. Esto es necesario para asegurarse de que el objeto original no se altere. Este método no es compatible con navegadores antiguos como IE y solo funciona en navegadores modernos.

Eche un vistazo a esta guía para obtener más información sobre el Object.assign() método.

Operador de propagación

El operador de propagación (... ) es otra característica más de ES6 que proporciona una forma sencilla de realizar una clonación superficial de un objeto, equivalente a lo que Object.assign() hace:

const fruits = { mango: '🥭️', apple: '🍎' };

const moreFruits = { ...fruits };

console.log(moreFruits);
// { mango: '🥭️', apple: '🍎' }

Aunque los operadores de distribución existen desde ES6 (ESMAScript 2015), la compatibilidad con la clonación de objetos se introdujo recientemente en ES9 (ESMAScript 2018). Por lo tanto, solo debe considerar usar este enfoque para las últimas versiones de los navegadores modernos.

Métodos JSON

Si su objeto solo contiene tipos primitivos y no incluye objetos anidados o externos, matrices, Date objetos, funciones, etc., puede crear fácilmente un clon profundo del objeto utilizando métodos JSON:JSON.stringify() y JSON.parse() :

const fruits = { mango: '🥭️', apple: '🍎' };

const moreFruits = JSON.parse(JSON.stringify(fruits));

console.log(moreFruits);
// { mango: '🥭️', apple: '🍎' }

Este enfoque funciona muy bien en todos los navegadores modernos e IE8+. Sin embargo, hay dos inconvenientes:

  • El objeto debe ser compatible con el formato JSON. Esto significa que los objetos anidados deben ser JSON serializables y deserializables.
  • Es más lento que otras soluciones cuando el objeto contiene muchas propiedades.

Los métodos JSON solo admiten cadenas, números y objetos literales sin funciones ni propiedades de símbolos. Vería un comportamiento extraño cuando el objeto contiene valores no compatibles:

// undefined is omitted
// Infinity is turned to null
JSON.parse(JSON.stringify({ a: undefined, b: Infinity })); 

// { b: null }

// Date object is turned to string
JSON.parse(JSON.stringify({ a: new Date() })); 

// { a: "2020-06-16T19:44:57.492Z" }

// function is omitted too
JSON.parse(JSON.stringify({ a: () => { return 'Hi'; } })); 

// {}

Deberías solo utilice este enfoque para objetos compatibles con JSON. Para los objetos que contienen valores incompatibles con JSON, considere usar una biblioteca de terceros como Lodash para crear un clon profundo.

El cloneDeep() de Lodash Método

Lodash proporciona el cloneDeep() método que copia recursivamente todo en el objeto original al nuevo objeto. Funciona para todos los tipos de datos, incluidas funciones, objetos anidados, matrices y símbolos.

Aquí hay un ejemplo:

const _ = require('lodash');

const obj = {
    name: 'John Doe',
    age: 45,
    address: {
        city: 'Berlin',
        country: 'DE'
    },
    job: undefined,
    credits: Infinity
};

const cloned = _.cloneDeep(obj);

console.log(cloned);

// {
//     name: 'John Doe',
//     age: 45,
//     address: { city: 'Berlin', country: 'DE' },
//     job: undefined
//     credits: Infinity
// }

Para obtener más información sobre objetos, prototipos y clases de JavaScript, consulte este artículo.

Leer a continuación: Cómo copiar una matriz en JavaScript