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

{% outerLink href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in" text="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 上追蹤我們