Clonar un objeto implica crear una copia independiente que pueda modificarse sin afectar al objeto original.
Esto es especialmente importante porque en JavaScript todos objetos son tipos de referencia.
Es decir, que cuando asignas un objeto a una nueva variable, ambas variables apuntan al mismo objeto en memoria. Si modificas una, la otra también se verá afectada:
const original = { nombre: "Juan", edad: 25 };
const copia = original;
copia.nombre = "María";
console.log(original.nombre); // "María" (el objeto original también cambió)
Para evitar efectos colaterales no deseados, es necesario clonar objetos en lugar de simplemente copiar la referencia.
Para esta entrada, es muy importante que entendáis lo que es un tipo valor y un tipo referencia
Clonación superficial
La clonación superficial (shallow copy) crea una nueva instancia de un objeto y copia las propiedades de nivel superior.
Sin embargo, si alguna de estas propiedades es un objeto en sí, solo se copiará la referencia.
Método | Tipo de clonación | Anidadas | Valores especiales | Complejidad |
---|---|---|---|---|
Operador de propagación | Superficial | ❌ | ❌ | Baja |
Object.assign() | Superficial | ❌ | ❌ | Baja |
Usando el operador de spread ...
El operador de propagación (...
) es una forma moderna y concisa de clonar un objeto. Este método crea una copia superficial del objeto, lo que significa que copia las propiedades de nivel superior, pero no los objetos o arrays anidados.
const original = { nombre: "Juan", edad: 25 };
const clon = { ...original };
clon.nombre = "María";
console.log(original.nombre); // "Juan" (el original no se modifica)
Sin embargo, si el objeto tiene propiedades anidadas, estas seguirán compartiendo referencias con el original:
const original = { nombre: "Juan", detalles: { edad: 25, ciudad: "Madrid" } };
const clon = { ...original };
clon.detalles.ciudad = "Barcelona";
console.log(original.detalles.ciudad); // "Barcelona" (se afecta el original)
Usando Object.assign()
Object.assign()
es otra forma de realizar una clonación superficial. Este método copia las propiedades propias de un objeto (no las heredadas) al objeto de destino.
const original = { nombre: "Juan", edad: 25 };
const clon = Object.assign({}, original);
clon.nombre = "María";
console.log(original.nombre); // "Juan"
Al igual que el operador de propagación, este método no clona objetos anidados.
Clonación profunda
La clonación profunda (deep copy) crea una copia completa de un objeto, incluyendo todos los objetos anidados.
Esto asegura que las modificaciones en la copia no afecten al objeto original (es la clonación “de verdad”)
Método | Tipo de clonación | Anidadas | Valores especiales | Complejidad |
---|---|---|---|---|
structuredClone() | Profunda | ✅ | ✅ | Baja |
JSON.parse(JSON.stringify) | Profunda | ✅ | ❌ | Media |
_.cloneDeep() (Lodash) | Profunda | ✅ | ✅ | Media |
Clonación manual | Depende | ✅ | Depende | Alta |
Usando structuredClone()
A partir de ES2021, JavaScript introdujo el método structuredClone
, que permite realizar clonaciones profundas de manera nativa.
const original = { nombre: "Juan", detalles: { edad: 25, ciudad: "Madrid" } };
const clon = structuredClone(original);
clon.detalles.ciudad = "Barcelona";
console.log(original.detalles.ciudad); // "Madrid"
Ventajas
- Maneja valores complejos como
Date
,Map
,Set
, y referencias circulares. - Es más rápido que soluciones basadas en
JSON
.
Limitaciones
- No está disponible en versiones anteriores de JavaScript.
- Puede no estar soportado en todos los navegadores.
Usando JSON
Una técnica sencilla para clonar objetos que no contienen métodos ni valores no serializables (como funciones, undefined
, o propiedades circulares) es usar JSON.stringify()
y JSON.parse()
.
const original = { nombre: "Juan", detalles: { edad: 25, ciudad: "Madrid" } };
const clon = JSON.parse(JSON.stringify(original));
clon.detalles.ciudad = "Barcelona";
console.log(original.detalles.ciudad); // "Madrid"
Limitaciones
- No funciona si el objeto contiene funciones, valores
undefined
, o propiedades circulares. - Convierte valores especiales como
Date
a strings.
Usando bibliotecas externas
Cuando necesitas una solución robusta para clonar objetos complejos, puedes usar bibliotecas como Lodash. El método cloneDeep
de Lodash realiza una clonación profunda completa.
import _ from "lodash";
const original = { nombre: "Juan", detalles: { edad: 25, ciudad: "Madrid" } };
const clon = _.cloneDeep(original);
clon.detalles.ciudad = "Barcelona";
console.log(original.detalles.ciudad); // "Madrid"
Lodash maneja correctamente objetos con referencias circulares y tipos no serializables.
Clonación manual
En ciertos casos, especialmente si el objeto tiene estructuras específicas o necesitas optimizar el rendimiento, puedes clonar un objeto manualmente recorriendo sus propiedades.
Clonación superficial manual
function clonarObjeto(objeto) {
const clon = {};
for (let clave in objeto) {
if (objeto.hasOwnProperty(clave)) {
clon[clave] = objeto[clave];
}
}
return clon;
}
const original = { nombre: "Juan", edad: 25 };
const clon = clonarObjeto(original);
clon.nombre = "María";
console.log(original.nombre); // "Juan"
Clonación profunda manual
function clonarProfundamente(objeto) {
const clon = {};
for (let clave in objeto) {
if (objeto.hasOwnProperty(clave)) {
if (typeof objeto[clave] === "object" && objeto[clave] !== null) {
clon[clave] = clonarProfundamente(objeto[clave]);
} else {
clon[clave] = objeto[clave];
}
}
}
return clon;
}
const original = { nombre: "Juan", detalles: { edad: 25, ciudad: "Madrid" } };
const clon = clonarProfundamente(original);
clon.detalles.ciudad = "Barcelona";
console.log(original.detalles.ciudad); // "Madrid"