iT邦幫忙

2022 iThome 鐵人賽

DAY 7
0
Modern Web

前端蛇行撞牆記系列 第 7

Day7 前端蛇行撞牆記 - 親愛的 forEach()

  • 分享至 

  • xImage
  •  

前言

昨天講到 iteration ,今天就來講forEach()

其實一開始在操作陣列方法的時候,比較常使用map(),因為一直有個印象就是forEach()是不會改變原陣列的,但後來發現陣列的value如果是物件的話,居然可以去改變到property的值?

WHAT?

當 value 是基本型別、物件型別

基本型別

先來試試看陣列裡面都是基本型別的話會是怎樣?來直接修改value。

const array = [1,2,3,4]

array.forEach((item) => item + 1)

console.log(array)
// [1, 2, 3, 4]

跟我認知的一樣,是不會改變的。

物件型別

物件型別來試試看,直接修改value的property id

const array = [ 
    { id:0 },
    { id:1 },
    { id:2 }
]

array.forEach(item => item.id + 1)

console.log(array)
// (3) [{…}, {…}, {…}]
// 0: {id: 0}
// 1: {id: 1}
// 2: {id: 2}

不會改變。

或者是在裡面重新賦值一個新的物件:

const array = [ 
    { id:0 },
    { id:1 },
    { id:2 }
]

array.forEach((item, index) => item = { xd: index })

console.log(array)
// (3) [{…}, {…}, {…}]
// 0: {id: 0}
// 1: {id: 1}
// 2: {id: 2}

OK 這些都是我認識的forEach(),不會被改變!

BUT! 如果我只去修改物件的property並再賦值給這個property的話呢?

const array = [ 
    { id:0 },
    { id:1 },
    { id:2 }
]

array.forEach(item => item.id = item.id + 1)

console.log(array)
// (3) [{…}, {…}, {…}]
// 0: {id: 1}
// 1: {id: 2}
// 2: {id: 3}

唔....改到了。

那剛剛修改基本型別是直接去改他的值,如果我用index去改呢?

let array = [1, 2, 3, 4]
array.forEach((item, index) => array[index] = index)

console.log(array)
// [0, 1, 2, 3]

我的天啊竟然也改到了....

討論

遍歷問題

  • forEach()是一個會遍歷的方法,如果在陣列裡面去直接改變value的話會有點危險,因為不確定這個陣列要變成怎樣?如果在裡面一直array.push(),這樣豈不是變成無窮迴圈了?

::危險勿試::
實測使用for...loop的確無法,我的devTools disconnect..

const array = [1,2,3,4]

for(let i = 0; i < array.length; i++) {
    array.push(i)
}

但是使用forEach()卻可以。不過很明顯的是他的確只遍歷四次,就像是forEach()在開始跑的時候已經確定現在的長度是多少,並不會因為有增加就多跑。

const array = [1,2,3,4]

array.forEach((item) => {
    array.push(item)
})

console.log(array)
// [1, 2, 3, 4, 1, 2, 3, 4]

儲存問題

  • forEach()在進行的時候是淺拷貝一個陣列來做,所以在forEach()的時候不會用原本的陣列,直接改變value也不是改變到原本的陣列
    => 但如果是用index / key 去找的話,就會直接找到指向的地址,而去改變真正的value的值
const array = [ 
    { id:0 },
    { id:1 },
    { id:2 }
]

array.forEach((item, index) => item.xd = index)

console.log(array)
// (3) [{…}, {…}, {…}]
// 0: {id: 0, xd: 0}
// 1: {id: 1, xd: 1}
// 2: {id: 2, xd: 2}

結論

真相只有一個 ------------->

  • 可以利用index, key來改到原本的陣列的reference

就是這樣 喵

後記

事實上當初會遇到這個問題是因為我在切to-do list的時候,使用者把資料儲存,畫面渲染資料要改變,就會直接使用forEach()去把每個的property來換成新的資料,這些都有被改到。

  tasks.forEach((task, index) => {
    if (task.id === Number(id)) {
      tasks[index].comment = taskComment.value;
      tasks[index].title = taskTitle.value;
      tasks[index].date = taskDate.value;
      tasks[index].datetime = taskDatetime.value;
      tasks[index].isFolded = !tasks[index].isFolded;
    }
  });

後來有一次想說直接用一個新的物件去賦值比較快,就直接這樣寫:

  tasks.forEach((task, index) => {
    if (task.id === Number(id)) {
      task = {
      comment: taskComment.value,
      title: taskTitle.value,
      date: taskDate.value,
      datetime: taskDatetime.value,
      isFolded: !tasks.isFolded
     }
    }
  });

結果卻沒改到!

於是開始思考forEach()到底有沒有辦法改到原本的陣列?理當是不行啊,那以前怎麼用都沒出事,還是一樣改到呢?

現在就知道他會如何被改變,如何不被改變,就不會突然大喊:「我以為他都是可以被改變的耶!!」,反而了然於心:「哼,我早就知道了,要怎麼對付你這傢伙!」

要改變的話就用index, key去改就好了啊!沒在怕!

今天就這樣了~明天見!

後記比結論長真的很好笑XD


參考資料:forEach 会改变原数组值吗?
foreach能否修改数据?
你还在以为forEach真的能改变原数组吗?你悟了吗?


上一篇
Day6 前端蛇行撞牆記 - 迭代(iteration)與她的姐妹們
下一篇
Day8 前端蛇行撞牆記 - 不用宣告一個空陣列也可以創建陣列的方法
系列文
前端蛇行撞牆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
Chris
iT邦新手 3 級 ‧ 2022-09-29 23:51:58

內心戲是你的重頭戲

我要留言

立即登入留言