JavaScript中for of和for in的差別

前言

JavaScript遍歷資料的方法有相當多種,本身資料若是陣列,甚至還有.forEach.map可以使用

主要功能都是遍歷數組,但在可讀性上會有較佳的感受。當然使用上也略有不同

不論採用哪種遍歷資料,都比最基礎的for(let i = 0;i < arr.length;i++)來得優美許多

本篇針對最容易搞混的for infor of說明

for in vs for of

主要差別如下:

  1. for in是ES5標準;for of是ES6標準。for of修復for in的不足
  2. 用object來說的話,for in遍歷的是key;for of遍歷的是value。不過JavaScript中object本身不是可迭代對象,for of會直接報錯(object迭代寫法後面再說明)
  3. for of不能遍歷一般物件,主要是針對數組(即array、arguments等可迭代的對象)使用,會忽略掉不可遍歷的對象
  4. for of可使用的對象有ArrayMapSetStringTypedArrayarguments
  5. for in會遍歷自定義屬性,甚至原型鏈上的屬性也會遍歷到,反而又不是我們所想要的

.forEach同時是在ES5中引進的用法,個人猜想當初設計的想法應該是:

數組要取value的話用.forEach,要取key的話用for in來取代最原始的for(let i = 0;i < arr.length;i++)寫法。分成兩種取法避免掉使用Object.key()這種比較複雜的寫法,從而精簡語意

.forEach僅能做單純的遍歷,不可以配合continuebreakreturn等語句,在實務上又沒那麼好用

而用for in遍歷衍生不少問題,最後只好針對for in又推出了修正版本的for of

以上只是個人的推測,若有錯誤煩請告知

直接上程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
let arr = ['a','b','c','d',{'e':'e_value','f':'f_value'}];

for(let index in arr){
console.log(index);
}
// 0,1,2,3,4
//---------------------------------------------------
//若想要用for in 取value,也是可以
for(let index in arr){
console.log(arr[index]);
}
//a,b,c,d,{'e':'e_value','f':'f_value'}
//---------------------------------------------------
//for in 會遍歷自定義屬性
arr.name='myArray';
for(let index in arr){
console.log(index);
}
// 0,1,2,3,4,name
//若寫console.log(arr[index]),自定義的name則會印出其內容'myArray'
//---------------------------------------------------
//但用for of,則可以寫的更優雅。且不會遍歷自定義屬性
for(let value of arr){
console.log(value);
}
//a,b,c,d,{'e':'e_value','f':'f_value'}

object 遍歷方法

前文可以看到for of修正了for in的問題,同時解決array中.forEach限制的continuebreak

但在JavaScript中,除了陣列,最常使用的就是object了!

直接將for of套用在object中,卻發現直接報錯!因為object本身不是一個可迭代的對象,所以無法遍歷!

這時候就要配合Object.keys()來做了

1
2
3
4
5
6
7
8
9
10
let obj = {
a:'a_value',
b:'b_value',
c:'c_value'
}

for(let key of Object.keys(obj)){
console.log(obj[key]);
}
//a_value,b_value,c_value

但這樣寫就太過冗長,不如就直接用for in來做

而這也是for in的主要工作

1
2
3
4
for(let idx in obj){
console.log(obj[idx]);
}
//a_value,b_value,c_value

延伸:陣列內含object的寫法

若您的資料結構較複雜,有一堆object組合而成的陣列,想要遍歷陣列裡的物件的value

這又該怎麼寫呢?

就要結合for infor of了!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let complexArray = [
{
name:'小明',
birth:'1995/07/01'
},
{
name:'小美',
birth:'1989/11/17'
},
{
name:'小帥',
birth:'1990/06/25'
}
]

for(let obj of complexArray){ //先遍歷出所有物件
for(let key in obj){ //再遍歷每一物件的key,取得value
console.log(obj[key]);
}
}
//小明,1995/07/01,小美,1989/11/17,小帥,1990/06/25

當然實務上不是只有單純的遍歷印出就好

這大概只有在將資料輸出成一份總表才有可能用上

若得到的數組資料後,還要先行過濾一些條件,再產生結果

只要將console.log(obj[key]);根據業務需求放入條件,回傳需要的結果就行了!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//比如列出6月出生的的人物資料
//日期判斷偷懶直接用正則表達來做,實務上配合moment.Js可以做得更好,好讀又易理解
for(let obj of complexArray){ //先遍歷出所有物件
for(let key in obj){ //再遍歷每一物件的key,取得value
if(key=='birth')
if(obj['birth'].match(/\d{4}\/06\/\d{2}/))
console.log(obj);
}
}
//以此題為例,只需要判斷其中日期,遍歷所有key其實很浪費資源又浪費時間
//若已知是針對日期(或其他key的內容)做判斷的話,其實只需要for of就夠了
//直接將要判斷key寫上去,不但增加可讀性、少跑一次迴圈、又好理解(一樣,日期方面配合moment.Js更佳)
for(let obj of complexArray){
if(obj['birth'].match(/\d{4}\/06\/\d{2}/))
console.log(obj);
}
//{name:'小帥',birth:'1990/06/25'}

後記

每次從Python回來再寫JavaScript的時候,都會搞混然後又要再Google一次…
因為Python是用for in
而JavaScript上大部份情況用for of較佳,但有的時候又會回來用for in

參考資料

javascript总for of和for in的区别?