hand Inicio
hand JSBloqs
hand GutenBloqs
Qué?
nochedía

DESARROLLO WEB con
REACT y WORDPRESS

Apúntate a la newsletter (escribo algo de tanto en cuanto)

Cómo copiar un object en JavaScript? no es tan sencillo como parece

600 palabras
3 minutos
May 29, 2020
blogjavascript

Copiando un objeto de forma "habitual" te copiará las referencias y no los valores, con lo que no tendrás un objeto efectivamente clonado. Para conseguirlo tienes básicamente 2 alternativas, utilizar una librería o JSONizar y desJSONizar

  1. Cuál es el problema (valor versus referencia)
  2. Copia simple
  3. Copia shallow (superficial)
  4. Copia deep

Cuál es el problema (valor versus referencia)

Copiar un object es algo que cuando queremos hacerlo es porque queremos una copia idéntica del objeto, pero independiente

Esa independencia no es fácil de conseguir

Por qué?

Porque lo que copiaremos serán referencias a variables, cuando lo que querríamos sería crear nuevas variables con los mismos valores

Para escenificarlo mira éste código

js
let myVar = 'hola'
let myVar2 = myVar
myVar = 'que tal'
console.log(myVar) // que tal
console.log(myVar2) // hola
Ver en CodeSandBox

Aquí pasan 2 cosas

  • Cuanodo asignamos una variable primita a otra variable primitiva, nunca se asignan referencias, siempre se asignan valores, con lo que myVar2 nunca contendrá una referencia a myVar, sino el valor que tenía en ese momento
  • Cuando hacemos la segunda asignación myVar = 'que tal', no estamos cambiando el valor del string que tenía la sino que estamos creando una nueva variable de tipo string

En cambio, si utilizamos cualquier estructura de datos (esto es, un valor no primitivo)

js
let myVar = ['hola']
let myVar2 = myVar
myVar[0] = 'que tal'
console.log(myVar) // ['que tal']
console.log(myVar2) // ['que tal']
Ver en CodeSandBox

Aquí vuelven a pasar dos cosas

  • Aquí myVar2 sí que guarda la referencia de myVar, en lugar de almacenar su valor
  • Y con la segunda asignación myVar[0] = 'que tal' aquí no estamos creando una nueva variable myVar sino que estamos modificando la variable

Esa variable myVar[0] sí que la estamos creando de nuevo, pero myVar se mantiene siendo la misma que la original

Puede parecer complicado, pero el símil es pensar en pisos y en direcciones de los pisos

Un string o cualquier otro valor primitivo representa un piso

Un array o un object o cualquier otro valor no primitivo representa la dirección de ese piso

Si copias un piso, quiere decir que tienes dos pisos

Si copias una dirección, quiere decir que tienes dos direcciones (en dos libretas) que las dos apuntan al mismo piso

Entonces, la pregunta es, si estoy con un valor tipo object que está lleno de direcciones de pisos, cómo puedo hacer una copia de las direcciones y de los pisos?

[ para arrays tienes una entrada aquí ]

Copia simple

Con esta copia lo que tenemos es un duplicado de la libreta donde apuntábamos las direcciones, pero los pisos son los mismos

js
let myObj = { site: 'https://www.kuworking.com/' }
console.log(myObj.site) // https://www.kuworking.com/
let myObjCopy = myObj
console.log(myObjCopy.site) // https://www.kuworking.com/
myObj.site = 'https://www.mevoyaotropiso.com'
console.log(myObj.site) // https://www.mevoyaotropiso.com
console.log(myObjCopy.site) // https://www.mevoyaotropiso.com
Ver en CodeSandBox

Por lo tanto, cuando cambiamos el piso, los dos objetos ven el piso actualizado porque estaban apuntando al mismo object

Copia shallow (superficial)

Por copia superficial se entiende una copia del objeto a un sólo nivel, esto es, si tenemos un objeto como el siguiente:

js
let myObj = {
site: 'https://www.kuworking.com/',
data: {
type: 'blog',
location: 'barcelona',
},
}

Una copia shallow nos dará un object que será una copia real en myObj y en myObj.site, pero más allá del primer nivel sólo serán referencias y por lo tanto myObj.data.type y myObj.data.location se referirán al objeto original

Esta copia superficial o shallow copy se hace con Object.assign()

js
let myObj = {
site: 'https://www.kuworking.com/',
data: {
type: 'blog',
location: 'barcelona',
},
}
let myObjCopy = Object.assign({}, myObj)
myObjCopy.site = 'https://www.mevoyaotropiso.com'
myObjCopy.data.type = 'mudanza'
console.log(myObj)
console.log(myObjCopy)
/*
{
"site": "https://www.kuworking.com/", >___> NO ha cambiado
"data": {
"type": "mudanza", >>>>> ha cambiado
"location": "barcelona"
}
}
{
"site": "https://www.mevoyaotropiso.com", >>>>> ha cambiado
"data": {
"type": "mudanza", >>>>> ha cambiado
"location": "barcelona"
}
}
*/
Ver en CodeSandBox

Como ves, el cambio de myObjCopy.data.type ha afectado a los dos objetos por igual, mientras que el cambio de myObjCopy.site sí que sólo ha afectado a la copia shallow del objeto myObjCopy

Copia deep

Para copiar todo el objeto, esto quiere decir todos los elementos contenidos en el objeto, lo idea sería tener una función dedicada para ello

Pero no la tenemos

El Object.assign debería ocupar ese sitio pero ya hemos visto que sólo nos da una copia de un nivel

Soluciones hay varias, desde hacer un loop recursivo hasta hacer lo que haremos en unas líneas

La primera opción te da la posibilidad de hacer una copia perfecta, incluyendo los métodos que pudieras tener en el objeto

Podrías hacerlo vía librería tipo loadash o fabricándote una función tu mismo

O puedes hacer lo siguiente:

  • convertir el objeto a texto, y luego volver a convertir el texto en objeto

De esta forma se pierden las referencias originales y se hace todo de nuevo, lo que equivale a hacer un objet totalmente nuevo, o lo que es lo mismo, un deep clone

Excepto los métodos, estos se pierden (si es que tienes)

Se hace con JSON.stringify y JSON.parse

js
let myObj = {
site: 'https://www.kuworking.com/',
data: {
type: 'blog',
location: 'barcelona',
},
}
let myObjCopy = JSON.parse(JSON.stringify(myObj))
myObjCopy.site = 'https://www.mevoyaotropiso.com'
myObjCopy.data.type = 'mudanza'
console.log(myObj)
console.log(myObjCopy)
/*
{
"site": "https://www.kuworking.com/",
"data": {
"type": "blog",
"location": "barcelona"
}
}
{
"site": "https://www.mevoyaotropiso.com",
"data": {
"type": "mudanza",
"location": "barcelona"
}
}
*/
Ver en CodeSandBox

Y efectivamente, ahora he cambiado todo el objeto-copia sin alterar el objeto-original

Si quieres intentar entender mejor si las variables en JavaScript se pasan por valor o por referencia puedes entretenerte con los comentarios en stack overflow

En realidad siempre se pasan por valor, pero ese valor puede almacenar un (valor) primitivo o una referencia

Qué tal la entrada?

👌 Bien 🙌🙌
👍 Bien, pero algunas cosas podrían explicarse mejor 😬
🤷‍♂️ Da por sentadas demasiadas cosas 😒
🤷‍♂️ A ver, hay poca chicha 😬
🤷‍♂️ Los ejemplos no son muy claros 🙇‍♂️
🤷‍♂️ No se entiende, está mal escrito 👎
✍️ Hay errores, revísalo en cuanto puedas 🙏
Enviar Feedback ✍️
El texto está en blanco!
Gracias por enviarme tu opinión
👍

Si quieres explorar más cursos y más entradas en el blog, los tienes todos en la página principal, y si el contenido te ha ayudado si lo compartes me ayudas a dar difusión 👍

Privacidad
by kuworking.com
[ 2020 >> kuworking ]