在循環 for、for-in、forEach、for-of 、map中改變item的值,會發生什麼?

語言: CN / TW / HK

theme: channing-cyan

聽説你精通循環,我不信

真正開始寫業務邏輯,就離不開循環。而循環一直是編程中基礎的基礎。但是作為一個工作多年的前端程序員,一定還有人不瞭解循環的基礎知識。

下面我們一起來看看,在循環中如果改變了item的值會發生什麼:

forEach

改變item本身

```js const list = [ {name: 'a', count: 1}, 2, function fn() { console.log(3); }, Symbol(), 'sss', [4,4,4,4], new Date() ]

list.forEach(item => { item = 3 })

console.log(list)

```

js [ { name: 'a', count: 1 }, 2, [Function: fn], Symbol(), 'sss', [ 4, 4, 4, 4 ], 2022-09-13T10:40:17.322Z ]

我們發現,基礎類型的幾個,string, number,Symbol()的內容都沒有發生變化。

改變item的屬性

``` const list = [ {name: 'a', count: 1}, 2, function fn() { console.log(3); }, Symbol(), 'sss', [4,4,4,4], new Date() ]

list.forEach(item => { item.count = 3 })

console.log(list) ```

[ { name: 'a', count: 3 }, 2, [Function: fn] { count: 3 }, Symbol(), 'sss', [ 4, 4, 4, 4, count: 3 ], 2022-09-13T10:41:26.631Z { count: 3 } ]

我們發現:

  • 基礎類型的,依舊沒有發生改變。

  • 引用類型的變量,如果自身帶了count屬性,該屬性就會被修改;如果不帶該屬性,就會添加count屬性。

for

改變item本身

由於for 循環裏,沒有專門的一個變量"item",可以獲取到對應的引用,我們只能用list[index]的形式去獲取到每一項。

我們運行看看效果。

```js const list = [ {name: 'a', count: 1}, 2, function fn() { console.log(3); }, [4,4,4,4], new Date() ]

for (let i = 0; i < list.length; i ++) { list[i] = 4 }

console.log(list)

```

```js [ 4, 4, 4, 4, 4 ]

```

全部被無差別覆蓋了。

改變item的屬性

```js const list = [ {name: 'a', count: 1}, 2, function fn() { console.log(3); }, [4,4,4,4], new Date() ]

for (let i = 0; i < list.length; i ++) { list[i].count = 4 }

console.log(list)

```

[ { name: 'a', count: 4 }, 2, [Function: fn] { count: 4 }, [ 4, 4, 4, 4, count: 4 ], 2022-09-13T10:44:50.164Z { count: 4 } ]

我們發現,和forEach的時候,表現一致:

  • 基礎類型的,依舊沒有發生改變。

  • 引用類型的變量,如果自身帶了count屬性,該屬性就會被修改;如果不帶該屬性,就會添加count屬性。

for-in

```js const list = [ {name: 'a', count: 1}, 2, function fn() { console.log(3); }, [4,4,4,4], new Date() ]

for(let i in list) { list[i] = 4 }

console.log(list)

```

js [ 4, 4, 4, 4, 4 ]

for in 其實和for循環一致,因為他們都是取到了index,然後修改list[index]

這裏就不分別看改變item和改變item屬性了。

for of

改變item本身

```js const list = [ {name: 'a', count: 1}, 2, function fn() { console.log(3); }, [4,4,4,4], new Date() ]

for(let i of list) { i = 4 }

console.log(list)

```

```js

[ { name: 'a', count: 1 }, 2, [Function: fn], [ 4, 4, 4, 4 ], 2022-09-13T10:56:11.711Z ]

``` 我們發現item無法別更改。

改變item的屬性

```js const list = [ {name: 'a', count: 1}, 2, function fn() { console.log(3); }, [4,4,4,4], new Date() ]

for(let i of list) { i.count = 4 }

console.log(list)

```

```js

[ { name: 'a', count: 4 }, 2, [Function: fn] { count: 4 }, [ 4, 4, 4, 4, count: 4 ], 2022-09-13T10:57:36.085Z { count: 4 } ]

```

我們發現:結果和forEach一致。他們都是在迭代函數裏拿到了item。

map

改變item本身

```js const list = [ {name: 'a', count: 1}, 2, function fn() { console.log(3); }, Symbol(), [4,4,4,4], new Date() ]

list.map(item => { item = 4 })

console.log(list)

```

```js [ { name: 'a', count: 1 }, 2, [Function: fn], Symbol(), [ 4, 4, 4, 4 ], 2022-09-13T11:01:10.614Z ]

```

我們發現,item無動於衷。

改變item的屬性

```js const list = [ {name: 'a', count: 1}, 2, function fn() { console.log(3); }, Symbol(), [4,4,4,4], new Date() ]

list.map(item => { item.count = 4 })

console.log(list) ```

js [ { name: 'a', count: 4 }, 2, [Function: fn] { count: 4 }, Symbol(), [ 4, 4, 4, 4, count: 4 ], 2022-09-13T10:59:53.050Z { count: 4 } ]

分析總結

| 方式 | 取值方式 | 改變自身 | 改變item的屬性 | |------- | ------------ | ---------------- | ------------- | | for | list[index] | 可以改變list[index] | 基礎類型不可以引用類型可以 | | for-in | list[index] | 可以改變list[index] | 基礎類型不可以引用類型可以 | | for-of | item | 不可以改變item | 基礎類型不可以引用類型可以 | | forEach | item | 不可以改變item | 基礎類型不可以引用類型可以 | | map | item | 不可以改變item | 基礎類型不可以引用類型可以

為什麼不可以改變屬性

改變自身和改變屬性,原因是一致的,就是分析一下,真正操作的數據,到底是不是原數據本身。

這裏,主要還是因為迭代器。

在for-of forEach map 方法中,其實item通過引用類型,指向了原來list裏面的每一項。

我們來看細節:

```js const list = [ {name: 'a', count: 1}, 2, function fn() { console.log(3); }, Symbol(), 'sss', [4,4,4,4], new Date() ] const iter = listSymbol.iterator

const firstElement = iter.next() console.log(firstElement) firstElement.value.count = 4 console.log(firstElement) console.log(firstElement.value === list[0]);

```

{ value: { name: 'a', count: 1 }, done: false } { value: { name: 'a', count: 4 }, done: false } true

對item進行操作,其實是對iterator.next() 指向的對象,也就是 iterator.next().value 進行了操作。

  • 如果原來的值是引用類型,那麼iterator.next().value 和 list[index] 表示的是同一個對象。操作的時候當然可以改變原來的item;
  • 如果原來的值是基礎類型,那麼iterator.next().value 和 list[index] 分別指向了一個基礎類型的值。操作的時候不會改變原來的item;