這里沒有太多解釋,所以我就直奔主題。
對于以下代碼塊:
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
為什么在元素迭代中重新分配值不會改變陣列中的物件?
item = {
...item,
...changes
}
但是通過使用索引訪問它來更改它確實會更改陣列中的物件嗎?
items2[i] = {
...item,
...changes
}
更新陣列中物件的最佳方法是什么?是items2[i]理想的嗎?
uj5u.com熱心網友回復:
對引數重新分配說不!
這是對 JavaScript 等高級語言的一種基本理解。
函式引數是給定值的臨時容器。
因此,任何“重新分配”都不會改變原始值。
例如看下面的例子。
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);
正如您所看到的,當您重新分配引數時,不會觸及原始值。甚至還有一個很好的Lint 規則,因為這是一個非常容易出錯的區域。
突變在這里會起作用,但是......
但是,如果您“變異”變數,這將起作用。
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);
所以回到你的代碼片段。在 foreach 回圈中,每個陣列項都會發生“函式呼叫”,其中將陣列項作為引數傳入。與上面類似,這里將起作用的是突變。
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)
但是,最好避免突變!
最好不要變異,因為這會導致更多錯誤。更好的方法是使用 map 并獲取全新的物件集合。
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
})
uj5u.com熱心網友回復:
Object.assign為此,我會選擇經典:
const items = [
{ id: 1, name: 'one' },
{ id: 2, name: 'two' },
];
const changes = {
name: 'hello'
}
items.forEach( (item) => Object.assign(item,changes) )
console.log(items)
如果目標物件中的屬性具有相同的鍵,則源中的屬性會覆寫它們。較晚來源的屬性會覆寫較早來源的屬性。 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
uj5u.com熱心網友回復:
正如 forEach 的 MDN 頁面所說:
forEach() 對每個陣列元素執行一次 callbackFn 函式;與 map() 或 reduce() 不同,它總是回傳未定義的值并且不可鏈接。典型的用例是在鏈的末端執行副作用。
看看這里:https : //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
這意味著盡管您確實為 item 創建了新物件,但它并未作為該陣列索引的值回傳。與您的第二個示例不同,第一個示例不會更改原始陣列,而只是創建新物件并回傳 undefined。這就是為什么你的陣列沒有被修改的原因。
uj5u.com熱心網友回復:
您可以采用的另一種方法是使用map并根據原始資料和更改創建一個新陣列:
const items = [
{ id: 1, name: 'one' },
{ id: 2, name: 'two' },
];
const changes = {
name: 'hello'
}
const newItems = items.map((item) => {
...item,
...changes
})
console.log(newItems);
但是如果你需要修改原始陣列,它要么通過索引訪問元素,要么Object.assign. 嘗試直接使用=運算子分配值不起作用,因為item引數是通過值而不是參考傳遞給回呼的- 您沒有更新陣列指向的物件。
uj5u.com熱心網友回復:
值不更新的原因是因為您正在重新分配區域變數,而不是重新分配陣列元素。
one = { id: 1, name: "one" }
two = { id: 2, name: "two" }
items = [one, two]
items 陣列不包含物件,而是包含對物件的參考。
one two
▲ ▲
items -> [ ┃ , ┃ ]
當你將一個陣列元素分配給一個區域變數時,這就是forEach呼叫中發生的事情,它也不包含物件而是一個對它的參考(這同樣適用于oneand two)。
item = items[0] // passed as the parameter to the `forEach` callback function
item ━? one
如果您重新分配,item您只是在更新區域變數并將其指向新的方向。沒有理由改變陣列。
item = { ...item, ...changes }
item ━? some_new_object
這是一個 irl 場景,可以讓您更好地了解情況:
假設我心中總是有一件是我最喜歡的物品(各種愿望清單)。我來參觀你的房子(陣列),真的很喜歡你墻上的時鐘。我決定那個鐘是我最喜歡的東西。一段時間后,我變老了,我最喜歡的東西變成了我在拐角處的商店里看到的其他時鐘(重新分配)。
我最喜歡的物品已經改變,但沒有理由用我最喜歡的新物品替換你家的時鐘。
在您的第二個場景中items[i] = { ...item, ...change },我會主動到您家來,用我最喜歡的時鐘替換您的時鐘。
作為旁注。map()JavaScript 中有一個函式可以遍歷陣列,并將所有元素替換為回呼的回傳值。這確實創建了一個新陣列。
const items = [
{ id: 1, name: 'one' },
{ id: 2, name: 'two' },
];
const changes = {
name: 'hello'
}
const newItems = items.map((item) => {
return {
...item,
...changes
};
});
console.log(newItems);
或者,如果您不介意改變陣列物件,您可以使用Object.assign()它將第二個引數的所有屬性分配給第一個。
const items = [
{ id: 1, name: 'one' },
{ id: 2, name: 'two' },
];
const changes = {
name: 'hello'
}
items.forEach((item) => {
Object.assign(item, changes);
// compairable with:
// for (const key in changes) {
// item[key] = changes[key];
// }
});
console.log(items);
這樣做的原因是因為我們沒有重新分配items. 相反,我們遵循參考并替換專案的屬性。因此,我沒有更改愿望清單上的專案,而是修改愿望清單中已有的當前專案。在這種情況下,這可能意味著為您的時鐘提供一些新的指標或更改側面數字的字體。
uj5u.com熱心網友回復:
如果您正在修改屬性,則可以使用它
items.forEach((item, i) => {
// items[i] = {
// ...item,
// ...changes
// }
item.name=changes.name;
});
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/363665.html
