Copias superficiales y profundas en JavaScript | Nicolás Garzón
Una copia superficial crea un contenedor exterior nuevo, pero conserva referencias hacia valores anidados. Una copia profunda intenta duplicar también la estructura interna.
JavaScript
Copiar const original = {
profile : {
city : "Bogotá" ,
} ,
} ;
const shallowCopy = {
... original,
} ; JavaScript
Copiar original !== shallowCopy;
original. profile === shallowCopy. profile;
JavaScript
Copiar const alias = original; Esto no crea una copia. alias y original contienen la misma referencia.
JavaScript
Copiar alias === original; Una copia superficial sí crea otro objeto exterior.
JavaScript
Copiar const copy = {
... original,
} ;
copy === original; JavaScript
Copiar const copy = {
... source,
} ; JavaScript
Copiar const copy = Object. assign ( { } , source) ; JavaScript
Copiar const copyWithSpread = [ ... items] ;
const copyWithSlice = items. slice ( ) ;
const copyWithFrom = Array. from ( items) ; Todas estas estrategias copian el contenedor, no los objetos interiores.
JavaScript
Copiar const original = {
name : "Nicolás" ,
skills : [ "JavaScript" , "TypeScript" ] ,
} ;
const copy = {
... original,
} ; Texto
Copiar original ──→ objeto A
├─ name: "Nicolás"
└─ skills ──→ array X
copy ─────→ objeto B
├─ name: "Nicolás"
└─ skills ──→ array XLos objetos exteriores son distintos. Ambos apuntan al mismo array.
JavaScript
Copiar const copy = {
... original,
profile : {
... original. profile,
} ,
} ; Esto duplica original y profile, pero cualquier nivel más profundo continúa compartido si no se copia también.
JavaScript
Copiar const original = {
profile : {
address : {
city : "Bogotá" ,
} ,
} ,
} ; Copiar solo profile no duplica address.
Una copia profunda crea una estructura independiente para los valores clonables que están anidados.
JavaScript
Copiar const deepCopy = structuredClone ( original) ; JavaScript
Copiar original !== deepCopy;
original. profile !== deepCopy. profile;
original. profile. address !== deepCopy. profile. address; No existe una única operación universal capaz de copiar correctamente cualquier valor imaginable. La estrategia depende del tipo de datos y del significado del objeto.
structuredClone utiliza el algoritmo de clonación estructurada del entorno.
JavaScript
Copiar const copy = structuredClone ( source) ; Puede trabajar con muchos valores que JSON no conserva correctamente, como:
Objetos y arrays.
Date.
Map.
Set.
ArrayBuffer y typed arrays.
RegExp.
Estructuras con referencias circulares.
JavaScript
Copiar const original = {
createdAt : new Date ( ) ,
tags : new Set ( [ "javascript" , "web" ] ) ,
} ;
const copy = structuredClone ( original) ;
copy. createdAt instanceof Date ;
copy. tags instanceof Set ; JavaScript
Copiar const node = {
name : "root" ,
} ;
node. self = node;
const copy = structuredClone ( node) ;
copy. self === copy; JSON no puede serializar esta estructura directamente.
structuredClone no clona algunos valores, entre ellos:
Funciones.
WeakMaps y WeakSets.
Ciertos objetos proporcionados por el entorno.
Nodos del DOM.
JavaScript
Copiar structuredClone ( {
handler ( ) { } ,
} ) ; Una instancia de una clase contiene más significado que sus propiedades visibles.
JavaScript
Copiar class User {
constructor ( name ) {
this . name = name;
}
greet ( ) {
return ` Hello, ${ this . name} ` ;
}
} No debes asumir que una clonación genérica conservará correctamente:
Su prototipo personalizado.
Sus métodos.
Invariantes del constructor.
Propiedades privadas.
Recursos externos.
Para objetos de dominio puede ser más correcto definir una operación explícita.
JavaScript
Copiar class User {
clone ( ) {
return new User ( this . name) ;
}
} O reconstruir desde datos serializables.
JavaScript
Copiar const copy = User. fromJSON ( user. toJSON ( ) ) ; Las copias comunes no conservan necesariamente:
Descriptores originales.
Getters y setters como funciones de acceso.
Propiedades no enumerables.
La cadena de prototipos personalizada.
Si necesitas preservar descriptores de un nivel:
JavaScript
Copiar const copy = Object. defineProperties (
Object. create ( Object. getPrototypeOf ( source) ) ,
Object. getOwnPropertyDescriptors ( source) ,
) ; Esto sigue siendo superficial respecto a valores anidados.
JavaScript
Copiar const copy = JSON . parse ( JSON . stringify ( source) ) ; Puede funcionar para datos JSON simples, pero no es un clonador general.
Date se convierte en string.
undefined se pierde en objetos.
undefined dentro de arrays se convierte en null.
Funciones y symbols se omiten.
NaN e infinitos se convierten en null.
Map y Set pierden su estructura.
BigInt produce un error.
Las referencias circulares producen un error.
Se pierde la identidad compartida entre referencias relacionadas.
JavaScript
Copiar const source = {
createdAt : new Date ( ) ,
value : undefined ,
} ;
const copy = JSON . parse ( JSON . stringify ( source) ) ; createdAt será string y value desaparecerá.
Usa JSON cuando realmente quieres serializar datos compatibles con JSON, no solo porque necesitas “una copia profunda”.
JavaScript
Copiar const sharedAddress = {
city : "Bogotá" ,
} ;
const source = {
billingAddress : sharedAddress,
shippingAddress : sharedAddress,
} ; Una clonación estructurada conserva la relación interna.
JavaScript
Copiar const copy = structuredClone ( source) ;
copy. billingAddress === copy. shippingAddress;
copy. billingAddress !== sharedAddress; La copia tiene su propia dirección, pero ambas propiedades continúan apuntando al mismo clon interno.
Algunos datos binarios pueden transferirse con structuredClone.
JavaScript
Copiar const buffer = new ArrayBuffer ( 1024 ) ;
const copy = structuredClone (
{ buffer } ,
{ transfer : [ buffer] } ,
) ; Después de transferirse, el buffer original queda separado de su contenido. Esto evita duplicar memoria, pero cambia el objeto de origen y debe utilizarse de forma intencional.
A menudo no necesitas copiar todo.
JavaScript
Copiar function createOrderSnapshot ( order ) {
return {
id : order. id,
status : order. status,
total : order. total,
customer : {
id : order. customer. id,
name : order. customer. name,
} ,
} ;
}
Expresa qué datos importan.
Evita copiar recursos innecesarios.
Puede excluir información sensible.
Reduce dependencia de la forma interna completa.
Una copia profunda debe recorrer una estructura y crear nuevos valores. Puede ser costosa para objetos grandes.
Antes de clonar, pregunta:
¿Necesito independencia completa?
¿Solo cambiará una rama?
¿Puedo utilizar structural sharing?
¿Necesito un snapshot serializable?
¿El valor contiene funciones, clases o recursos externos?
Para actualizaciones de estado suele ser mejor copiar únicamente el camino modificado que clonar toda la estructura.
JavaScript
Copiar function createEditableDraft ( product ) {
return structuredClone ( {
id : product. id,
name : product. name,
variants : product. variants,
metadata : product. metadata,
} ) ;
} Esto puede ser apropiado si los datos son clonables y el formulario necesita un borrador totalmente independiente.
Si el producto contiene instancias, funciones o recursos especiales, conviene construir el draft explícitamente.
Confundir asignar una referencia con copiar.
Pensar que spread duplica objetos anidados.
Copiar un solo nivel y llamar profundo al resultado.
Utilizar JSON como clonador universal.
Esperar que una copia genérica preserve clases y métodos.
Clonar toda una estructura para cambiar una sola propiedad.
Ignorar referencias circulares.
Suponer que todas las APIs de clonación conservan descriptores.
Transferir un buffer sin saber que el original queda separado.
Copiar información sensible o recursos que no deberían duplicarse.
Un alias comparte exactamente la misma referencia.
Una copia superficial crea un contenedor nuevo y comparte referencias anidadas.
Spread y Object.assign son superficiales.
structuredClone soporta muchos tipos y referencias circulares.
No clona funciones ni todos los objetos del entorno.
Las clases personalizadas necesitan cuidado especial.
JSON sirve para serialización compatible con JSON, no para cualquier clonación.
Una copia profunda tiene costo y no siempre es necesaria.
Copiar según el dominio suele ser más claro que duplicar todo.
¿Qué comparaciones son verdaderas?
JavaScript
Copiar const original = {
profile : {
city : "Bogotá" ,
} ,
} ;
const shallow = { ... original } ;
const deep = structuredClone ( original) ; Respuesta original !== shallow es true; original.profile === shallow.profile es true; original !== deep es true; original.profile !== deep.profile es true.
this explica cómo una función tradicional recibe su receptor según la forma en que se invoca.