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 上。