JavaScript 中有哪些陣列 (Array) 的遍歷方法(for loop, for...in, for…of, forEach, map, filter, every, some)
2023年2月1日
在考察 JavaScript 的基礎能力面試中,經常會問到遍歷陣列的方法。本篇文章集合了,JavaScript 當中最常使用到的陣列遍歷方法,包括 for 迴圈、陣列的原生方法(forEach, map…),還有面試當中常見的陣列遍歷考題。
本篇主要分成兩大段落:
- 遍歷陣列的 8 種方法
- for...of和- for...in有什麼差別?
遍歷陣列 8 種方法
for loop
- 好處是可以使用 break結束循環或使用continue跳出當前迴圈
- 命令式的寫法比較冗長
let arr = [0, 1, 2, 3, 4, 5];
for (let i = 0; i < arr.length; i++) {
  if (i > 2) break;
  console.log(arr[i]);
}
// 0
// 1
// 2
for…of
- ES6 之後出現的語法,與 for迴圈相比寫法較簡潔
- 與 for迴圈一樣,可以搭配break、continue和return使用
const arr = ["apple", "orange", "banana"];
for (const item of arr) {
  if (item === "orange") continue;
  console.log(item);
}
// apple
// banana
for...in
- 遍歷的是鍵值 (key),而在陣列中的鍵值 (key)就是索引 (index)
const arr = ["apple", "orange", "banana"];
for (const item in arr) {
  console.log(item);
}
// 0
// 1
// 2
以下介紹陣列的原生方法
forEach
- 會針對每一個元素執行提供的函式 
- 聲明式的寫法較為精簡,但是不能使用 - break和- continue語法跳出循環,如下圖 
- forEach方法只會遍歷原陣列,並不會回傳一個新陣列。所以如果需要從一個舊陣列中建構出另一個新陣列,應該使用- map方法
map
- 針對每一個元素執行提供的函式,並回傳新陣列
const arr = [1, 2, 3, 4];
const newArr = arr.map((x) => x + 1);
console.log(newArr);
// [2,3,4,5]
console.log(arr);
// [1,2,3,4] // 原陣列不會被改變
filter
- 創建回傳一個新陣列,並過濾掉沒有通過所提供函式的元素
const arr = [19, 2, 13, 40];
const newArr = arr.filter((x) => x > 18);
console.log(newArr);
// [19, 40]
console.log(arr);
// [19, 2, 13, 40] // 原陣列不會被改變
every
- 會測試陣列中每一個元素是否通過提供的函式,最終返回一個 布林值(Boolean)
- 如果每個元素都通過,最終會回傳 true,但若其中有一個元素測試值為false,那就會提前結束,並回傳false
- 與 some的差別在於,用every方法,需要陣列當中每一個都通過測試的函式,最終才會回傳true
[12, 5, 8, 130, 44].every((x) => x > 10); // false
[12, 54, 18, 130, 44].every((x) => x > 10); // true
some
- 方法類似於 every,會測試陣列每一個元素是否通過提供的函式,若其中有一個元素測試值為true,那就會提前結束,並回傳true
- 與 every的差別在於,用some方法,只要陣列當中有一個元素通過測試函式,就會回傳true
[2, 5, 8, 1, 4].some((x) => x > 10); // false
[12, 5, 8, 1, 4].some((x) => x > 10); // true
延伸題:哪些方法會改變原陣列?
以上提拱的幾種陣列方法,預設上並不會直接改變原陣列,除非我們傳入的函式針對原陣列做一些處理。
延伸題:哪些方法會回傳新的陣列?
map 與 filter 會回傳新的陣列,因此如果是在 React 等要求寫不可變 (immutable) 的程式碼時,經常需要用到這兩個方法。
延伸題:請問 for 循環 和 forEach 的差別?
- 寫法上差異,forEach較為簡潔
- forEach不能提早結束整遍歷,也不能搭配- break和- continue語法使用,使用- return也會被忽略
延伸題:請問 for...of 和 for...in 的差別?
- for...of遍歷的是陣列中的- 元素值 (value);而- for...in是遍歷的是- 鍵值 (key),換句話說- for... in是遍歷陣列中的- 索引 (Index)- var arr = [10, 20, 30]; for (let value of arr) { console.log(value); } //10 20 30 var arr = [10, 20, 30]; for (let value in arr) { console.log(value); } //0 1 2
- 大部分情況, - for...in會被拿來遍歷物件,但並不建議使用- for...in遍歷陣列,主要原因有以下 4 點:- 當陣列中有空項目時,使用 - for...in方法會忽略該項目。- let a = ["a", , "c"]; for (let x in a) { console.log(x); } // 0 2 for (let x of a) { console.log(x); } // a undefined c
- for... in會檢查對象的屬性是否 enumerable ,如果 true ,會把這些屬性名稱全部迭代出來,因為某些 JavaScript 套件可能會在 Array 原型上建立方法,此時如果使用- for...in方法可能會操作到不是存在於陣列中的值。 但是用- for...of可以避免這問題。- Array.prototype.foo = 1; let a = [1, 2, 3]; for (let x in a) { console.log(x); } // 0 1 2 foo
- for...in遍歷的鍵值(key)雖然是- Index,但- for...in會以 String 型別作為鍵值(key),因此我們如果拿- Index去做運算,可能會造成預期外的結果。- let a = [1, 2, 3]; for (let x in a) { console.log(x + 1); } // 01 11 21
- for...in的遍歷並不保證順序是正確的,這樣的狀況在陣列上非常難使用,因為當我們想迭代陣列中的值時,就是希望順序能夠正確。而- for...of會先檢查對象- [Symbol.iterator]這個屬性,接著使用- [Symbol.iterator].next()一個個迭代出值,來確保順序正確。但由於 Object 並不具備- [Symbol.iterator]這個屬性,所以- for...of並不能使用在 Object 上。