JavaScript 中有哪些陣列 (Array) 的遍歷方法(for loop, for...in, for…of, forEach, map, filter, every, some)

2023年2月1日

💎 加入 E+ 成長計畫 與超過 300+ 位軟體工程師一同在社群中成長,並且獲得更多的軟體工程學習資源

在考察 JavaScript 的基礎能力面試中,經常會問到遍歷陣列的方法。本篇文章集合了,JavaScript 當中最常使用到的陣列遍歷方法,包括 for 迴圈、陣列的原生方法(forEach, map…),還有面試當中常見的陣列遍歷考題。

本篇主要分成兩大段落:

  • 遍歷陣列的 8 種方法
  • for...offor...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 迴圈一樣,可以搭配 breakcontinuereturn 使用
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 語法跳出循環,如下圖

    Uncaught SyntaxError: Illegal break statement
  • 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

延伸題:哪些方法會改變原陣列?

以上提拱的幾種陣列方法,預設上並不會直接改變原陣列,除非我們傳入的函式針對原陣列做一些處理。

延伸題:哪些方法會回傳新的陣列?

mapfilter 會回傳新的陣列,因此如果是在 React 等要求寫不可變 (immutable) 的程式碼時,經常需要用到這兩個方法。

延伸題:請問 for 循環 和 forEach 的差別?

  • 寫法上差異,forEach 較為簡潔
  • forEach 不能提早結束整遍歷,也不能搭配 break 和  continue 語法使用,使用 return 也會被忽略

延伸題:請問 for...offor...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 點:

    1. 當陣列中有空項目時,使用 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
      
    2. 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
      
    3. 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
      
    4. for...in 的遍歷並不保證順序是正確的,這樣的狀況在陣列上非常難使用,因為當我們想迭代陣列中的值時,就是希望順序能夠正確。而 for...of 會先檢查對象 [Symbol.iterator] 這個屬性,接著使用 [Symbol.iterator].next() 一個個迭代出值,來確保順序正確。但由於 Object 並不具備 [Symbol.iterator] 這個屬性,所以 for...of 並不能使用在 Object 上。

🧵 如果你想收到最即時的內容更新,可以在 FacebookInstagram 上追蹤我們