No hay mucha explicación aquí, así que iré directo al grano.
Para el siguiente bloque de código:
const items = [ { id: 1, name: 'one' }, { id: 2, name: 'two' }, ]; const changes = { name: 'hello' } items.forEach((item, i) => { item = { ...item, ...changes } }) console.log(items) // items NOT reassigned with changes items.forEach((item, i) => { items[i] = { ...item, ...changes } }); console.log(items) // items reassigned with changes
¿Por qué reasignar los valores directamente en la iteración del elemento no cambia los objetos en la matriz?
item = { ...item, ...changes }
pero cambiarlo accediendo a él con el índice cambia los objetos en la matriz?
items2[i] = { ...item, ...changes }
y ¿cuál es la mejor manera de actualizar objetos en una matriz? es items2[i]
ideal?
Esta es una especie de comprensión fundamental de lenguajes de alto nivel como JavaScript.
Los parámetros de función son contenedores temporales de un valor dado.
Por lo tanto, cualquier "reasignación" no cambiará el valor original.
Por ejemplo, mira el ejemplo de abajo.
let importantObject = { hello: "world" } // We are just reassigning the function parameter function tryUpdateObjectByParamReassign(parameter) { parameter = { ...parameter, updated: "object" } } tryUpdateObjectByParamReassign(importantObject) console.log("When tryUpdateObjectByParamReassign the object is not updated"); console.log(importantObject);
Como puede ver, cuando reasigna un parámetro, el valor original no se modificará. Incluso hay una buena regla de pelusa, ya que esta es un área muy propensa a errores.
Sin embargo, si "muta" la variable, esto funcionará.
let importantObject = { hello: "world" } // When we mutate the returned object since we are mutating the object the updates will be shown function tryUpdateObjectByObjectMutation(parameter) { parameter["updated"] = "object" } tryUpdateObjectByObjectMutation(importantObject) console.log("When tryUpdateObjectByObjectMutation the object is updated"); console.log(importantObject);
Así que volviendo a tu fragmento de código. En un bucle foreach, lo que sucede es una "llamada de función" por cada elemento de la matriz donde el elemento de la matriz se pasa como parámetro. Tan similar a lo anterior, lo que funcionará aquí es como mutación.
const items = [ { id: 1, name: 'one' }, { id: 2, name: 'two' }, ]; const changes = { name: 'hello' } items.forEach((item, i) => { // Object assign just copies an object into another object Object.assign(item, changes); }) console.log(items)
Es mejor no mutar, ya que esto puede generar aún más errores . Un mejor enfoque sería usar el mapa y obtener una nueva colección de objetos.
const items = [{ id: 1, name: 'one' }, { id: 2, name: 'two' }, ]; const changes = { name: 'hello' } const updatedItems = items.map((item, i) => { return { ...item, ...changes } }) console.log({ items }) console.log({ updatedItems })
Como dice la página de MDN para forEach:
forEach() ejecuta la función callbackFn una vez para cada elemento de la matriz; a diferencia de map() o reduce(), siempre devuelve el valor indefinido y no se puede encadenar. El caso de uso típico es ejecutar efectos secundarios al final de una cadena.
Eche un vistazo aquí: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
Esto significa que aunque creó un nuevo objeto para el elemento, no se devolvió como un valor para ese índice de matriz. A diferencia de su segundo ejemplo, el primero no cambia la matriz original, sino que solo crea nuevos objetos y devuelve indefinidos. Es por eso que su matriz no se modifica.
Iría con un Object.assign
clásico para esto:
const items = [ { id: 1, name: 'one' }, { id: 2, name: 'two' }, ]; const changes = { name: 'hello' } items.forEach( (item) => Object.assign(item,changes) ) console.log(items)
Las propiedades del objeto de destino se sobrescriben con las propiedades de los orígenes si tienen la misma clave. Las propiedades de las fuentes posteriores sobrescriben las anteriores. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign