Qué es un deep clone
En JavaScript, al asignar un objeto a otra variable se copia la referencia, no el contenido. Cambiar el original afecta a la copia y viceversa. Cuando necesitamos un duplicado independiente de una estructura compleja hablamos de deep clone (clonado profundo).
const original = { user: { name: "Alan" } };
const copy = original;
copy.user.name = "Otro";
console.log(original.user.name); // "Otro" — era la misma referencia
Durante años el "truco" más común fue JSON.parse(JSON.stringify(x)). Funciona para objetos y arrays con datos básicos, pero falla con fechas, mapas, conjuntos, tipados binarios y cualquier referencia circular. Por eso ES2022 estandarizó structuredClone, una función global que utiliza el mismo algoritmo interno que usan los workers para pasar mensajes.
const state = { user: { name: "Alan" }, createdAt: new Date() };
const clone = structuredClone(state);
clone.user.name = "Otro";
console.log(state.user.name); // "Alan" — independiente
console.log(clone.createdAt instanceof Date); // true
structuredClone está disponible en Node.js 17+ y en todos los navegadores modernos, sin importar nada.
Tipos que sí maneja
El algoritmo de clonado estructurado soporta una lista bastante extensa de tipos. Los más relevantes:
- Primitivos:
string,number,boolean,null,undefined,BigInt. - Objetos planos y arrays, incluidas propiedades con claves Symbol registradas.
Date(con la fecha exacta).RegExp(fuente y flags).MapySet, clonando claves y valores en profundidad.ArrayBuffer,TypedArrayyDataView.Blob,File,FileList(en navegadores).- Cualquier combinación anidada de los anteriores y referencias circulares.
Date, Map, Set y RegExp
Estos son los casos en los que JSON.parse(JSON.stringify(...)) falla de forma clara:
const data = {
createdAt: new Date("2026-01-15T10:00:00Z"),
tags: new Set(["js", "ts", "node"]),
scores: new Map([["alan", 10], ["noa", 9]]),
pattern: /^\d{3}-\d{2}$/
};
const jsonCopy = JSON.parse(JSON.stringify(data));
console.log(jsonCopy.createdAt instanceof Date); // false (pasa a string)
console.log(jsonCopy.tags instanceof Set); // false (queda {})
console.log(jsonCopy.pattern instanceof RegExp); // false (queda {})
const sc = structuredClone(data);
console.log(sc.createdAt instanceof Date); // true
console.log(sc.tags); // Set(3) { 'js', 'ts', 'node' }
console.log(sc.scores.get("alan")); // 10
console.log(sc.pattern.test("123-45")); // true
Tipos binarios
Los buffers se clonan byte a byte, lo que permite duplicar bloques de datos de forma eficiente:
const buffer = new Uint8Array([1, 2, 3, 4]);
const copy = structuredClone(buffer);
copy[0] = 99;
console.log(buffer[0]); // 1 — copia independiente
Referencias circulares
Una estructura autorreferencial hace saltar un error a JSON.stringify, pero structuredClone la replica sin problema:
const node = { label: "root" };
node.self = node;
node.children = [node];
const clone = structuredClone(node);
console.log(clone === clone.self); // true
console.log(clone.children[0] === clone); // true
console.log(clone !== node); // true — son estructuras distintas
El algoritmo usa una tabla interna para recordar qué objetos ya ha clonado, lo que le permite respetar la identidad dentro del clon.
Tipos que NO se clonan
No todo se puede copiar. structuredClone lanza un DataCloneError cuando encuentra:
- Funciones, tanto funciones propias como métodos. Si una clase usa métodos en el prototipo, el clon seguirá funcionando porque el prototipo no se copia, pero las funciones almacenadas como valores de propiedad no son clonables.
- Prototipos personalizados. El clon es un objeto plano con los mismos datos; pierde la relación con tu clase. No hay
instanceof MiClasedespués de clonar. - Nodos del DOM,
Window,HTMLElement,Event, etc. - Cualquier objeto "host" no soportado (p. ej. handles de Node).
Ejemplo del problema con clases:
class User {
constructor(name) { this.name = name; }
greet() { return `Hola, ${this.name}`; }
}
const u = new User("Alan");
const clone = structuredClone(u);
console.log(clone.name); // "Alan"
console.log(clone instanceof User); // false
// console.log(clone.greet()); // TypeError: clone.greet is not a function
Si necesitas que el clon sea de la misma clase, combina structuredClone con una reconstrucción explícita:
const data = structuredClone({ name: u.name });
const cloneAsUser = new User(data.name);
Y el caso clásico del function member:
const obj = { onClick: () => console.log("click") };
// structuredClone(obj); // DataCloneError: () => {} could not be cloned
Opción transfer: mover en lugar de copiar
Algunos tipos binarios admiten ser transferidos en lugar de copiados. Tras la transferencia, el original queda vacío pero no se duplica memoria. Es útil con buffers grandes:
const buffer = new ArrayBuffer(1024 * 1024); // 1 MB
const clone = structuredClone({ data: buffer }, { transfer: [buffer] });
console.log(buffer.byteLength); // 0 (ya no pertenece al original)
console.log(clone.data.byteLength); // 1048576
Después de la transferencia, cualquier acceso al buffer original está detached. Úsalo solo cuando puedas garantizar que el emisor ya no necesita el recurso.
Comparación con JSON.parse(JSON.stringify)
| Característica | JSON.parse(JSON.stringify(x)) | structuredClone(x) |
|----------------|------------------------------------|------------------------|
| Objetos y arrays sencillos | Sí | Sí |
| Date | No (se convierte a string ISO) | Sí |
| Map / Set | No (se pierden) | Sí |
| RegExp | No | Sí |
| ArrayBuffer / TypedArray | No | Sí |
| Referencias circulares | Lanza error | Sí |
| Funciones | Se descartan | DataCloneError |
| Prototipo personalizado | Se pierde | Se pierde |
| Rendimiento | Generalmente más lento | Optimizado nativamente |
| Disponibilidad | Siempre | Node 17+ y navegadores modernos |
Para casi cualquier deep clone nuevo, structuredClone es la opción recomendada. Solo se recurre a JSON en entornos muy antiguos o cuando se necesita además serializar a cadena para enviar por red.
Casos prácticos
Estado inmutable en gestores de estado
Una forma muy habitual de aplicar actualizaciones inmutables es clonar, mutar la copia y reasignar:
function setUserName(state, name) {
const draft = structuredClone(state);
draft.user.name = name;
return draft;
}
const next = setUserName({ user: { name: "Alan" } }, "Noa");
console.log(next.user.name); // "Noa"
Es más claro que componer a mano el nuevo árbol con spread, especialmente cuando hay varios niveles.
Snapshot para deshacer (undo)
Para implementar un historial simple, basta con guardar clones del estado relevante:
const history = [];
function snapshot(state) {
history.push(structuredClone(state));
}
function undo() {
history.pop();
return history[history.length - 1];
}
Aislar datos externos
Cuando recibes datos de una librería y quieres trabajar con una copia propia para no mutar estructuras ajenas, un structuredClone al principio es la forma más directa.
Buenas prácticas
- Prefiere
structuredClonesobreJSON.parse(JSON.stringify(...))si el entorno es moderno (Node 17+, navegadores actuales). Es más correcto y soporta más tipos. - Recuerda que el clon pierde el prototipo: si necesitas conservar la clase, reconstruye manualmente la instancia.
- No intentes clonar objetos con funciones como propiedades. Sepáralos: guarda los datos en un objeto plano y las funciones aparte.
- Para clonar solo una parte de un objeto grande, construye antes el subobjeto mínimo; el algoritmo recorre todo lo que le pases.
- Usa
transferúnicamente con buffers que estés seguro de no volver a necesitar. - Si la estructura es sencilla y el rendimiento es crítico, pruébalo siempre: en algunos motores un clon manual con spread puede ser más rápido.
Resumen
structuredClone moderniza el deep clone en JavaScript. Con una sola llamada obtenemos copias independientes de estructuras que incluyen Date, Map, Set, RegExp, ArrayBuffer y referencias circulares, sin el coste ni las pérdidas de información de JSON.parse(JSON.stringify(...)). Sus limitaciones son conocidas y razonables: no clona funciones, prototipos ni nodos DOM. Integrado en Node 17+ y en todos los navegadores recientes, es la opción estándar para copia profunda en código nuevo.
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, JavaScript es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de JavaScript
Explora más contenido relacionado con JavaScript y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
- Entender qué es un deep clone y por qué se necesita.
- Usar
structuredClonepara duplicar estructuras complejas. - Clonar tipos que se pierden con JSON:
Date,Map,Set,TypedArray,RegExp,ArrayBuffer. - Manejar referencias circulares sin caer en bucles infinitos.
- Conocer las limitaciones: funciones, prototipos personalizados, nodos DOM.
- Transferir recursos con la opción
transferpara evitar copias innecesarias.
Cursos que incluyen esta lección
Esta lección forma parte de los siguientes cursos estructurados con rutas de aprendizaje