Al programar con JavaScript muchas veces nos enfrentamos al problema de copiar y modificar un objeto sin mutar el objeto original. ¿Por qué? ¿Cómo podemos resolverlo? ¿Acaso hay una forma novedosa de hacerlo? (Si la hay, "structuredClone").
Una variable puede contener uno de los siguientes valores primitivos:
Y nosotros podemos duplicarla con otro nombre y modificarla libremente sin ningún problema:
let persona1 = 'Soy Mike' let persona2 = a persona2 = 'Soy Becky' console.log(persona1) // 'Soy Mike' console.log(persona2) // 'Soy Becky'
Hasta aquí una variable funciona como una cajita en la cual podemos guardar valores de manera independiente. A cada variable le corresponde un valor.
Sin embargo, todo cambia cuando declaramos un objeto, ya que creamos una referencia a un espacio de la memoria donde se ha guardado el objeto.
let persona1 = { nombre: 'Mike' } let persona2 = persona1 persona2.nombre = 'Becky' console.log(persona1.nombre) // 'Becky' console.log(persona2.nombre) // 'Becky'
¿Por qué se modificó el nombre de persona1 al cambiar el de persona 2? Porque a persona2 le asignamos la referencia a persona1.
¿Cómo solucionamos esto? ¿Como duplicamos un objeto y cambiamos ciertas propiedades sin afectar el original?
Existen dos soluciones "superficiales" y dos "profundas", vamos a analizarlas.
let persona1 = { nombre: 'Mike', hobby: "bailar" } let persona2 = { ...persona1 } persona2.nombre = 'Becky'
let persona1 = { nombre: 'Mike', hobby: "bailar" } let persona2 = Object.assign({}, persona1) persona2.nombre = 'Becky'
En ambos casos tendremos dos "personas" que comparten un hobby, pero con nombres diferentes y podremos utilizar ambos objetos independientemente.
console.log(`Soy ${persona1.nombre} y amo ${persona1.hobby}`) // Soy Mike y amo bailar console.log(`Soy ${persona2.nombre} y amo ${persona2.hobby}`) // Soy Becky y amo bailar
La limitación del operador spread y de Object.assign
la encontramos cuando nuestro objeto tiene otros objetos anidados, por ejemplo:
let persona1 = { nombre: 'Mike', hobby: "bailar", pais: { nombre: "México", } } let persona2 = { ...persona1 } persona2.nombre = 'Becky' persona2.pais.nombre = 'Inglaterra' console.log(`Soy ${persona1.nombre} y naci en ${persona1.pais.nombre}`) // Soy Mike y naci en Inglaterra console.log(`Soy ${persona2.nombre} y naci en ${persona2.pais.nombre}`) // Soy Becky y naci en Inglaterra
Como puedes ver pudimos modificar el nombre exitosamente, sin embargo, al tratar de modificar el país que se encuentra en un objeto anidado volvimos al problema anterior donde los objetos no son totalmente independientes. En estos casos tenemos que utilizar copias "profundas" del objeto:
Es la manera comúnmente aceptada de clonar un objeto profundamente sin recurrir a librerías como lodash
let persona1 = { nombre: 'Mike', hobby: "bailar", pais: { nombre: "México", } } let persona2 = JSON.parse(JSON.stringify(persona1)) persona2.nombre = 'Becky' persona2.pais.nombre = 'Inglaterra' console.log(`Soy ${persona1.nombre} y naci en ${persona1.pais.nombre}`) // Soy Mike y naci en Mexico console.log(`Soy ${persona2.nombre} y naci en ${persona2.pais.nombre}`) // Soy Becky y naci en Inglaterra
El método globalstructuredClone() genera una copia profunda de un objeto empleando un algoritmo de clonación estructurada que copia recursivamente un objeto apoyándose de un mapa de referencias para evitar bucles infinitos.
Su funcionamiento interno suena rebuscado, pero es muy elegante en la práctica.
let persona1 = { nombre: 'Mike', hobby: "bailar", pais: { nombre: "México", } } let persona2 = structuredClone(persona1) persona2.nombre = 'Becky' persona2.pais.nombre = 'Inglaterra' console.log(`Soy ${persona1.nombre} y naci en ${persona1.pais.nombre}`) // Soy Mike y naci en Mexico console.log(`Soy ${persona2.nombre} y naci en ${persona2.pais.nombre}`) // Soy Becky y naci en Inglaterra
Ya está disponible en los navegadores más modernos, (excepto Safari) sin embargo, ya hay polyfills
para utilizarlo en navegadores antiguos
Puedes consultar toda la documentación al respecto aquí.
Cuéntame que te pareció el post y sobre que te gustaría que hablara en mis redes sociales o en Discord.
¡Sé valiente y No te rindas!