Original article: JS Copy an Object – How to Clone an Obj in JavaScript

Un objeto JavaScript es una colección de pares clave-valor. Es un tipo de datos no primitivo que puede contener varios tipos de datos. Por ejemplo:

const usuario = {
  nombre: "John Doe",
  edad: 14,
  verificado: false
};

Cuando trabaja con objetos en JavaScript, es posible que en ocasiones desee cambiar el valor o agregar una nueva propiedad al objeto.

En algunos escenarios, antes de actualizar o agregar nuevas propiedades, querrá crear un nuevo objeto y copiar o clonar los valores del original.

Por ejemplo, si desea copiar el valor del objeto usuario y luego cambiar el nombre a algo diferente. En el sentido típico, querrás utilizar el operador de igualdad (=).

const nuevoUsuario = usuario;
console.log(nuevoUsuario); // {nombre: 'John Doe', edad: 14, verificado: false}

Todo parece estar bien, pero veamos qué pasa si editamos nuestro segundo objeto:

const nuevoUsuario = usuario;
nuevoUsuario.nombre = "Jane Doe";

console.log(nuevoUsuario); 
// {nombre: 'Jane Doe', edad: 14, verificado: false}

Todo está bien con el nuevo objeto, pero si intentas verificar los valores de tu objeto original, notarás que se ve afectado. ¿Por qué? ¿Cómo?

console.log(usuario); // {nombre: 'Jane Doe', edad: 14, verificado: false}

Este es el problema. El objeto original se ve afectado porque los valores de un objeto son pasados por referencia. Esto significa que cualquier valor que almacene en el clon o en el objeto original apunta al mismo objeto, a la misma dirección de memoria.

Esto no es lo que se busca. Se desea almacenar el valor de un objeto en un objeto nuevo y manipular el valor en el objeto nuevo sin afectar el objeto original.

En este artículo, aprenderá tres métodos que puede utilizar para hacer esto. También aprenderá qué significan los clones profundos y superficiales y cómo funcionan.

En caso de que tengas prisa, aquí tienes los tres métodos y un ejemplo de cómo funcionan.

// Usando el Operador de Propagación (Spread Operator)
let clon = { ...usuario }

// Usando Object.assign()
let clon = Object.assign({}, usuario)

// Usando JSON.parse()
let clon = JSON.parse(JSON.stringify(usuario))

Si no tienes prisa, comencemos.🚀

¿Cómo clonar un objeto en JavaScript usando el Operador de Propagación (Spread Operator)?

El operador de propagación se introdujo en ES6 y puede distribuir valores en un objeto con la sintaxis de tres puntos: ...

// declaramos un objeto
const usuario = {
  nombre: "John Doe",
  edad: 14,
  verificado: false
};

// clonamos el objeto usando el operador de propagación
let clonUsuario = { ...usuario };

console.log(clonUsuario); 
// {nombre: 'John Doe', edad: 14, verificado: false}

Ya no se hace referencia directa al objeto, lo que significa que cambiar el valor del objeto no afectará al objeto original.

// clonamos el objeto usando el operador de propagación
let clonUsuario = { ...usuario };

// cambiamos un valor de clonUsuario
clonUsuario.nombre = "Jane Doe"

console.log(clonUsuario.nombre); // 'Jane Doe'
console.log(clonUsuario); // {nombre: 'Jane Doe', edad: 14, verificado: false}

Cuando verifica el valor del nombre en el objeto original o en todo el objeto, notará que no se ve afectado.

console.log(usuario.nombre); // 'John Doe'
console.log(usuario); // {nombre: 'John Doe', edad: 14, verificado: false}

Nota: Solo puede utilizar la sintaxis del operador de propagación para hacer una copia superficial de un objeto, no funciona con propiedades de objetos más profundos. Lo entenderás cuando lleguemos a la última sección de este artículo.

¿Cómo clonar un objeto en JavaScript usando el Object.assing()?

Una alternativa al operador de propagación es el método Object.assing(). Utilice este método para copiar los valores y propiedades de uno o más objetos de origen a un objeto de destino.

// declaramos un objeto
const usuario = {
  nombre: "John Doe",
  edad: 14,
  verificado: false
};

// clonamos el objeto usando Object.assing()
let clonUsuario = Object.assign({}, usuario);

console.log(clonUsuario); 
// {nombre: 'John Doe', edad: 14, verificado: false}

Ya no se hace referencia al objeto, lo que significa que cambiar el valor del objeto no afectará al objeto original.

// clonamos el objeto usando Object.assing()
let clonUsuario = Object.assign({}, usuario);

// cambiamos un valor del objeto clonado
clonUsuario.nombre = "Jane Doe"

console.log(clonUsuario.nombre); // 'Jane Doe'
console.log(clonUsuario); // {nombre: 'Jane Doe', edad: 14, verificado: false}

Cuando verifica el valor del nombre en el objeto original o en todo el objeto, notará que no se ve afectado.

console.log(usuario.nombre); // 'John Doe'
console.log(usuario); // {nombre: 'John Doe', edad: 14, verificado: false}

Nota: Solo puede utilizar el método Object.assign() para hacer una copia superficial de un objeto, no funciona con propiedades de objetos más profundos. Lo entenderás cuando lleguemos a la última sección de este artículo.

¿Cómo clonar un objeto en JavaScript usando el JSON.parse()?

El método final es JSON.parse(). Utilizará este método junto con JSON.stringify(). Puedes usar esto para clonar profundamente, pero tiene algunas desventajas. Primero, veamos cómo funciona.

// declaramos un objeto
const usuario = {
  nombre: "John Doe",
  edad: 14,
  verificado: false
};

// clonamos el objeto usando JSON.parse()
let clonUsuario = JSON.parse(JSON.stringify(usuario));

console.log(clonUsuario); 
// {nombre: 'John Doe', edad: 14, verificado: false}

Además, al igual que los métodos anteriores, ya no se hace referencia al objeto original. Esto significa que puede cambiar un valor en el nuevo objeto sin afectar el objeto original.

// clonamos el objeto usando JSON.parse()
let clonUsuario = JSON.parse(JSON.stringify(usuario));

// cambiamos un valor del objeto clonUsuario
clonUsuario.nombre = "Jane Doe"

console.log(clonUsuario.nombre); // 'Jane Doe'
console.log(clonUsuario); // {nombre: 'Jane Doe', edad: 14, verificado: false}

Cuando verifica el valor del nombre en el objeto original o en todo el objeto, notará que no se ve afectado.

console.log(usuario.nombre); // 'John Doe'
console.log(usuario); // {nombre: 'John Doe', edad: 14, verifido: false}

Nota: Este método se puede utilizar para la clonación profunda, pero no será la mejor opción porque no funciona con propiedades de que almacenan funciones o símbolos.

Exploremos ahora la clonación superficial y profunda y cómo puede utilizar el método JSON.parse() para realizar una clonación profunda. También aprenderás por qué no es la mejor opción.

Clonación superficial vs. clonación profunda

Hasta ahora, el ejemplo utilizado en este artículo es un objeto básico con un solo nivel. Esto significa que sólo hemos realizado clones superficiales. Pero cuando un objeto tiene más de un nivel, se le pedirá que realice una clonación profunda.

// objeto superficial
const usuario = {
  nombre: "John Doe",
  edad: 14,
  verificado: false
};

// objeto profundo
const usuario = {
  nombre: "John Doe",
  edad: 14,
  estado: {
    verificado: false,
  }
};

Observe que el objeto profundo tiene más de un nivel porque hay otro objeto en el objeto usuario. Un objeto profundo puede tener tantos niveles como quieras.

Nota: Cuando utiliza el operador de propagación o el método Object.assign() para clonar un objeto profundo, se hará referencia a los objetos más profundos.

const usuario = {
  nombre: "John Doe",
  edad: 14,
  estado: {
    verificado: false,
  }
};

// clanamos el objeto usando el operador de propagación 
let clonUsuario = { ...usuario };

// cambiamos un valor de clonUsuario
clonUsuario.estado.verificado = true;

console.log(clonUsuario); 
// {nombre: 'John Doe', edad: 14, estado: {verificado: true}}
console.log(usuario); 
// {nombre: 'John Doe', edad: 14, estado: {verificado: true}}

Notarás que tanto el objeto original como el nuevo se ven afectados porque cuando usas el operador de propagación o el método Object.assign() para clonar un objeto profundo, se hará referencia a los objetos más profundos.

¿Cómo podemos solucionar este problema?

Puede utilizar el método JSON.parse() y todo funcionará bien.

const usuario = {
  nombre: "John Doe",
  edad: 14,
  estado: {
    verificado: false,
  }
};

// clonamos el objeto usando JSON.parse()
let clonUsuario = JSON.parse(JSON.stringify(usuario));

// cambiamos un valor de clonUsuario
clonUsuario.estado.verificado = true;

console.log(clonUsuario); 
// {nombre: 'John Doe', edad: 14, estado: {verificado: true}}
console.log(usuario); 
// {nombre: 'John Doe', edad: 14, estado: {verificado: false}}

Pero hay un problema con este método. El problema es que puedes perder tus datos. ¿Cómo?

JSON.stringify() funciona muy bien con tipos de datos primitivos como números, cadenas o booleanos, y eso es lo que has visto en nuestros ejemplos anteriores. Pero a veces, JSON.stringify() es impredecible si no se conoce algunos valores y cómo se los maneja.

Por ejemplo, no funciona con funciones, símbolos o valores undefined. También cambia otros valores como NaN e Infinity a null, rompiendo tu código. Cuando tenga una función, símbolo o valor undefined, devolverá un par clave-valor vacío y lo omitirá.

const usuario = {
  nombre: "John Doe",
  edad: 14,
  estado: {
    verificado: false,
    metodo: Symbol(),
    titulo: undefined
  }
};

// clonamos el objeto usando Object.parse()
let clonUsuario = JSON.parse(JSON.stringify(userDetails));

Todo parece funcionar bien, pero para el nuevo objeto, JSON.stringify() no devolverá ningún par clave-valor para los valores undefinedysymbol.

console.log(clonUsuario); 

// salida
{
  nombre: "John Doe",
  edad: 14,
  estado: {
    verificado: false
  }
};


Esto significa que debes tener cuidado. La mejor opción para implementar la clonación profunda será utilizar Lodash. Entonces puede estar seguro de que no se perderá ninguno de sus datos.

const usuario = {
  nombre: "John Doe",
  edad: 14,
  estado: {
    verificado: false,
    metodo: Symbol(),
    titulo: undefined
  }
};

console.log(_.cloneDeep(usuario));

Para finalizar...


Este artículo le ha enseñado cómo clonar un objeto en JavaScript utilizando tres métodos principales. Has visto cómo funcionan esos métodos y cuándo utilizar cada uno de ellos. También aprendiste sobre la clonación profunda.

Puede leer este artículo para comprender por qué JSON.parse(JSON.stringify()) es una mala práctica para clonar un objeto en JavaScript.

¡Feliz codificación!