[JS] for、for...in、forEach、for...of 的差別
JavaScript 的 for 迴圈有好幾種用法,這次來研究其中的差異。
範例
本篇文章的範例程式碼都會以下列的物件、陣列為範例。
1 | // 普通陣列 |
普通陣列
for
for 是最基本的迴圈寫法,透過陣列的索引(index)取值
1
2
3
4
5
6
7for (let i = 0; i < arr.length; ++i) {
console.log(arr[i]);
}
// output:
// 1
// 2
// 3for 可以自行設定迴圈執行的區間,也可以用 break 來中斷。
1
2
3
4
5
6
7
8for (let i = 1; i < arr.length - 1; i += 1) {
console.log(arr[i]);
if (arr[i] > 1) {
break;
}
}
// output:
// 2
for…in
for…in 與 for 都是以索引值來造訪陣列。
for…in 可以用 break 來中斷,但是不能設定開始與結束的索引值。
1
2
3
4
5
6
7
8
9for (let i in arr) {
console.log(arr[i]);
if (arr[i] > 1) {
break;
}
}
// output:
// 1
// 2
forEach
forEach 是陣列方法,預設的第一個參數會取得陣列的值。
forEach 不能被中斷、也不能設定開始與結束的索引值。
類陣列(Array-like)不一定有 forEach 的方法(NodeList 有、Arguments 沒有)。
1
2
3
4
5
6
7arr.forEach((item) => {
console.log(item);
});
// output:
// 1
// 2
// 3forEach 可以選擇性的加上第 2、3 個參數來取得索引值和原始陣列。
1
2
3
4
5
6
7arr.forEach((item, index, array) => {
console.log(`${item}, ${index}, ${array}`);
});
// output:
// 1, 0, 1,2,3
// 2, 1, 1,2,3
// 3, 2, 1,2,3
for…of
for…of 直接取得陣列的值,沒有索引值。
for…of 可以用 break 來中斷,但是不能設定開始與結束的索引值。
1
2
3
4
5
6
7
8
9for (const item of arr) {
console.log(item);
if (item > 1) {
break;
}
}
// output:
// 1
// 2使用 for…of 取得索引值需要先使用 entries 方法產生迭代器物件(iterator)。
1
2
3
4
5
6
7for (const [index, item] of arr.entries()) {
console.log(`${index}, ${item}`);
}
// output:
// 0, 1
// 1, 2
// 2, 3
帶有空值的陣列
for 遇到空值會回傳 undefined。
for…in 遇到空值會跳過。
forEach 遇到空值會跳過。
for…of 遇到空值會回傳 undefined。
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
27
28
29for (let i = 0; i < arrWithEmpty.length; i++) {
console.log(arrWithEmpty[i]);
}
// output:
// 1
// undefined
// 3
for (let i in arrWithEmpty) {
console.log(arrWithEmpty[i]);
}
// output:
// 1
// 3
arrWithEmpty.forEach((item) => {
console.log(item);
});
// output:
// 1
// 3
for (const item of arrWithEmpty) {
console.log(item);
}
// output:
// 1
// undefined
// 3
帶有物件屬性的陣列
for 不會取得屬性的值
for...in 會取得屬性的值
forEach 不會取得屬性的值
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
27
28
29
30
31
32for (let i = 0; i < arrWithAttr.length; i++) {
console.log(arrWithAttr[i]);
}
// output:
// 1
// 2
// 3
for (let i in arrWithAttr) {
console.log(arrWithAttr[i]);
}
// output:
// 1
// 2
// 3
// a
arrWithAttr.forEach((item) => {
console.log(item);
});
// output:
// 1
// 2
// 3
for (const item of arrWithAttr) {
console.log(item);
}
// output:
// 1
// 2
// 3
物件
for 無法直接操作物件,需要先產生迭代器物件(iterator)。
for...in 可以取得物件的 key
。forEach 無法直接操作物件,需要先產生迭代器物件(iterator)。
for…of 無法直接操作物件,需要先產生迭代器物件(iterator)。
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
27
28
29
30
31
32const objKeys = Object.keys(obj);
for (let i = 0; i < objKeys.length; i += 1) {
console.log(obj[objKeys[i]]);
}
// output:
// 1
// 2
// 3
for (let i in obj) {
console.log(i);
}
// output:
// a
// b
// c
Object.keys(obj).forEach((item) => {
console.log(obj[item]);
});
// output:
// 1
// 2
// 3
for (const item of Object.values(obj)) {
console.log(item);
}
// output:
// 1
// 2
// 3
其他補充
- 使用 for、for…in、for…of 時,
請勿使用 var
來宣告裡面的變數(早期的文章都會使用 var,因為那個時候只有 var),請使用 let 或 const 來避免污染全域環境。 - Vue 的 v-for 撰寫範例通常都是
v-for="item in items
,但 官方文件 裡面有寫到可以使用v-for="item of items
來撰寫,這會更貼近 JavaScript 迭代的語法。 - forEach 方法會傳入一個 callback function,如果使用箭頭函式會有 this 指向的問題需要注意。
- forEach 不適合搭配非同步使用,forEach 不會等待非同步完成才進入下一個循環,會以同步的方式執行所有內容,使用 for、for…in、for…of 搭配 async、await 較佳。
參考文章: