logo
Published on

JavaScript 中迭代数组的多种方式

Authors
  • Name
    Twitter

在 JavaScript 中,有多种方法可以用来循环遍历数组。本文将介绍这些方法,包括 for-of 循环、forEach 方法、传统的 for 循环以及其他不常见的方式,同时探讨它们在处理异步操作时的不同表现。

快速总结

最常用的几种方式:

* for-of 循环 (仅限 ES2015+)

  • 它简单易用,并且 async 友好。

    for (const element of theArray) {
      // 使用 element 变量
    }
    

* forEach 方法 (仅限 ES5+)

  • 它非常适合同步操作,但不支持异步操作。

    theArray.forEach((element) => {
      // 使用 element 变量
    })
    

* 传统的 for 循环

  • 虽然较为老旧,但它支持异步操作。

    for (let index = 0; index < theArray.length; index++) {
      const element = theArray[index]
      // 使用 element 变量
    }
    

* 使用小心的 for-in 循环

  • 它同样支持异步操作,但不建议用于数组循环。

    for (const propertyName in theArray) {
      if (theArray.hasOwnProperty(propertyName)) {
        const element = theArray[propertyName]
        // 使用 element 变量
      }
    }
    

不推荐的几种方式:

  • 不要使用 for-in,除非你能够确保用适当的保障措施或清楚其可能引发的问题。
  • 不要使用 map 方法,如果你不需要它的返回值。map 方法是用来生成一个新数组的,而不是为了简单地遍历一个数组。
  • 不要使用 forEach,如果你的回调函数包含异步操作,并且你希望 forEach 等待这些操作完成(因为它不会等)。

接下来,我们将深入探讨各种循环方法的具体用法和特点。

针对真正的数组

1. 使用 for-of 循环 (仅限 ES2015+)

for-of 循环是 ES2015 添加的一种基于迭代器的循环方式。数组是可迭代对象,因此可以使用 for-of 循环进行遍历:

const a = ['a', 'b', 'c']
for (const element of a) {
  console.log(element)
}
// 输出:a
//      b
//      c

for-of 循环简洁明了,并且是 async 友好的:

async function processArray(array) {
  for (const element of array) {
    await someAsyncFunction(element)
  }
}

2. 使用 forEach 方法 (仅限 ES5+)

forEach 方法在 ES5 中引入,它适用于同步代码,不适用于异步操作:

const a = ['a', 'b', 'c']
a.forEach((element) => {
  console.log(element)
})

需要注意的是,forEach 不等待异步回调完成:

a.forEach(async (element) => {
  await someAsyncFunction(element)
  console.log(element)
})

上面的代码不会按顺序执行异步操作。

3. 使用传统的 for 循环

传统的 for 循环既支持同步也支持异步操作:

const a = ['a', 'b', 'c']
for (let index = 0; index < a.length; index++) {
  const element = a[index]
  console.log(element)
}

在异步环境中:

async function processArray(array) {
  for (let index = 0; index < array.length; index++) {
    await someAsyncFunction(array[index])
  }
}

4. 使用 for-in 循环(慎用)

使用 for-in 循环遍历数组时需慎重,因为它会遍历对象的所有可枚举属性,包括继承属性:

const a = ['a', 'b', 'c']
for (const key in a) {
  if (a.hasOwnProperty(key)) {
    const element = a[key]
    console.log(element)
  }
}

5. 显式使用迭代器 (仅限 ES2015+)

有时你可能需要显式使用迭代器:

const a = ['a', 'b', 'c']
const iterator = a.values()

let result = iterator.next()
while (!result.done) {
  console.log(result.value)
  result = iterator.next()
}

这种方式在异步环境中特别有用:

async function processArray(array) {
  const iterator = array.values()
  let result = iterator.next()
  while (!result.done) {
    await someAsyncFunction(result.value)
    result = iterator.next()
  }
}

针对类数组对象

除了真正的数组,JavaScript 也有类数组对象,如 NodeList 实例,HTMLCollection 实例,arguments 对象等。

使用大部分上述选项

现代浏览器中大部分数组的循环方法对类数组对象都适用:

  1. 使用 for-of 循环 (仅限 ES2015+)

    const nodes = document.querySelectorAll('div')
    for (const node of nodes) {
      console.log(node)
    }
    
  2. 使用 forEach 方法 (仅限 ES5+)

    const nodes = document.querySelectorAll('div')
    Array.prototype.forEach.call(nodes, (node) => {
      console.log(node)
    })
    
  3. 使用传统的 for 循环

    const nodes = document.querySelectorAll('div')
    for (let i = 0; i < nodes.length; i++) {
      console.log(nodes[i])
    }
    

转换为真正的数组

有时你可能希望将类数组对象转换为真正的数组,可以使用以下方法:

  1. 使用 Array.from (仅限 ES2015+)

    const nodesArray = Array.from(document.querySelectorAll('div'))
    
  2. 使用展开语法 (仅限 ES2015+)

    const nodesArray = [...document.querySelectorAll('div')]
    
  3. 使用 Array.prototype.slice.call

    const nodesArray = Array.prototype.slice.call(document.querySelectorAll('div'))
    

总结

JavaScript 提供了多种方法来循环遍历数组和类数组对象,每种方法有其特定的适用场景和优缺点。在选择具体方法时,应根据需要考虑同步或异步操作的要求,以及代码的可读性和执行效率。