我們先來看下第一段代碼:
const arr1 = [
{ id: 1, value: 1 },
{ id: 2, value: 2 },
{ id: 3, value: 3 },
{ id: 4, value: 4 },
{ id: 5, value: 5 },
]
arr1.forEach((item, index) => {
if (item.id === 1) {
item.value *= 10
}
})
將第一段代碼放入瀏覽器控制枱執行完之後,打印數組arr1
[
0: {id: 1, value: 10}
1: {id: 2, value: 2}
2: {id: 3, value: 3}
3: {id: 4, value: 4}
4: {id: 5, value: 5}
]
接着我們來看下第二段代碼:
const arr2 = [
{ id: 1, value: 1 },
{ id: 2, value: 2 },
{ id: 3, value: 3 },
{ id: 4, value: 4 },
{ id: 5, value: 5 },
]
arr2.forEach((item, index) => {
if (item.id === 1) {
item = { id: 6, value: 6 }
}
})
將第二段代碼放入瀏覽器控制枱執行完之後,打印數組arr2:
[
0: {id: 1, value: 1}
1: {id: 2, value: 2}
2: {id: 3, value: 3}
3: {id: 4, value: 4}
4: {id: 5, value: 5}
]
我們可以發現arr2中 [id = 1] 的元素並沒有被替換成我們期望
{ id: 6, value: 6 }
當然,最後我會説怎麼在forEach遍歷過程中去替換原數組中的元素,現在我們先來討論下,為什麼會出現 [id = 1] 的元素為什麼沒有被替換掉:
要想探究原因,我們就得將上述數組用照妖鏡照一照,看看它的真面目究竟是如何
js中的數組類型,分為兩大類,
一類是值類型 數值、布爾值、null、undefined
一類是引用類型 對象、數組、函數
看下面代碼,簡單説明js字面量創建對象的過程 :
let obj = { id: 1, name: 2 }
# 虛擬機在執行到 { id: 1, name: 2 } 就會在內存中創建一個對象
# 這個對象在內存中的地址假如為 0x1110,再將此地址賦值給變量obj
# 此時obj的值實際為0x1110
理解了上述js創建過程,那麼我們來揭開arr2數組的真實面紗
# 內存地址是16進制數表示,以下地址僅為説明問題
const arr2 = [
0x1110, 0x1111, 0x1112, 0x1113, 0x1114
]
看到arr2的真實面目,相信各位小夥伴應該明白了,上面展示的第二段代碼為什麼無效
# 那麼我們就來看看為什麼下面item沒有被替換
arr2.forEach((item, index) => {
# 我們在遍歷的過程中,我們拿到的item值其實是
# 0x1110, 0x1111, 0x1112, 0x1113, 0x1114 5個地址
if (item.id === 1) {
# { id: 6, value: 6 } 是對象的字面量創建方式,會在內存中
# 創建一個對象,並返回對象地址 假如為 0x1115
item = { id: 6, value: 6 }
# 我們再將 item = { id: 6, value: 6 } 語句翻譯一下
# 0x1110 = 0x1115
# 看到問題了嗎?將一個16進制數賦值給另外一個16進制數
# 其實這段是無效的賦值,也就被瀏覽器給忽略了,所以我們在
# 打印出得結果中發現 元素{id: 1, value: 1}並沒有被
# 替換成{id: 6, value: 6}
}
})
可能有些初學小夥伴有疑問,既然我們在遍歷的過程中拿到的是一個內存地址,那麼在最開始第一段代碼中,為什麼又能將元素{ id: 1, value: 1 }的value值改變為10呢,那麼請給我留言,不在這裏展開這個問題。
最後總結,forEach和map遍歷中 如果想改原數組,通過下面方式就行了
arr.forEach((item, index) => {
if (condition) {
arr[index] = something;
}
})
之所以會聊到這個問題,也是團隊小夥伴談到forEach遍歷過程中不能修改原數組,但是卻不知道為什麼不能修改,以及真的要修改,又如何去修改。後面想了下,可能也有其他的小夥伴有時也不太清楚,索性寫了篇小記,幫助理解。