Skip to content

JavaScript 遍历

for 遍历的 四个 方法

for、for in、for of、foreach

直接遍历 字符串 的方法有(两个):for、for of

直接遍历 对象 的方法有(一个):for in (只有 for in)

直接遍历 数组 的方法有(三个):for、for of、forEach (除了 for in)

转数组再遍历 对象 的方法:Object.entries(obj)、Object.keys(obj)、Object.values(obj).forEach()

for 遍历优缺点

for 循环

shell
优点:灵活,性能最佳,可以使用 break-终止循环 continue-跳过当前继续 控制流程(使用 return 终止会报错)
缺点:语法复杂
场景:遍历普通数组、字符串

for in

shell
优点:遍历对象可枚举的属性(包括原型链上的)
缺点:需要额外的 hasOwnProperty 检查来过滤原型属性
场景:只遍历对象

for of

shell
优点:遍历可迭代对象,可以使用 break continue
缺点:无索引,不能直接用于普通对象
场景:遍历数组、字符串、Map、Set、类数组、arguments、NodeList、generator 函数返回值

forEach

shell
优点:遍历部分迭代对象,函数式写法
缺点:无法使用 break continue(必须抛出异常或使用 return 模拟),无法遍历字符串
场景:遍历数组、Map, Set,类数组(NodeList,结合Array.from()遍历 arguments)

for 遍历总结

需要最高性能或精细控制:使用 for 循环

直接遍历对象属性:for...in(配合 hasOwnProperty)

遍历数组元素:for...of

函数式风格处理数组:forEach/map/filter/reduce

需要提前终止循环:避免 forEach,推荐使用 for/for...of

非 for 循环

特点:for 遍历是已经确定了循环次数,while 在不确定循环次数时也可用

while 循环:只有满足条件才会执行,不满足条件会终止,内部通常配合 i++ 来使用

do...while 循环:同上,只是需要先执行一次再判断

为何for比forEach快

for 是基于数组索引直接遍历计算,forEach 循环则是一种迭代器,对数组中的每个元素都创建一次回调函数,函数的创建也需要耗时

异步遍历

提示

  1. 对象的 for in 遍历 和 数组的 forEach 遍历都不支持异步写法
  2. 支持异步遍历的三种方法分别是:for、for of、for-await-of
  3. 异步遍历可以解决异步回调地狱
  4. 如果每一个异步函数之间没有相互依赖关系,只需最后一个异步执行完的统一结果,则直接上 Promise.all,更省时
  5. 如果每一个异步函数之间相互顺序依赖的话,那只能选择 for 异步
ts
 const p1 = () => {
     return new Promise((resolve,reject)=>{
         setTimeout(()=>{
             resolve('p1');
         },1000)
     })
 }
 const p2 = () => {
     return new Promise((resolve,reject)=>{
         setTimeout(()=>{
             resolve('p2');
         },1000)
     })
 }
 const p3 = () => {
     return new Promise((resolve,reject)=>{
         setTimeout(()=>{
             resolve('p3');
         },1000)
     })
 }

 const list = [p1,p2,p3]

方案一:for & Async

  1. for
ts
async function getAsyncResult (list){
   const resultList = []
   for (let i = 0; i < list.length; i++) {
      const result = await list[i]()
      console.log(result)
      resultList.push(result)
   }
   console.log(resultList)
}
getAsyncResult(list)
// 结果
// 1000ms --- p1
// 2000ms --- p2
// 3000ms --- p3 & ['p1', 'p2', 'p3']
  1. for of
ts
async function getAsyncResult (list){
   const resultList = []
   for (const item of list) {
      const result = await item()
      console.log(result)
      resultList.push(result)
   }
   console.log(resultList)
}
getAsyncResult(list)
// 结果
// 1000ms --- p1
// 2000ms --- p2
// 3000ms --- p3 & ['p1', 'p2', 'p3']
  1. for-await-of 循环(专门用于异步迭代)
ts
 async function getAsyncResult (list){
    const resultList = []
     for await (const item of list) {
         const result = await item()
         console.log(result)
         resultList.push(result)
     }
    console.log(resultList)
}
getAsyncResult(list)
// 结果
// 1000ms --- p1
// 2000ms --- p2
// 3000ms --- p3 & ['p1', 'p2', 'p3']

方案二:Promise.all

如果每一个异步函数之间没有相互依赖关系,只需最后一个异步执行完的统一结果,则直接上 Promise.all,得到最终结果的时间上要比 for 异步短,for 是无论如何都要排队的,Promise.all 则是并行

ts
// 注意list里面的值
const list = [p1(),p2(),p3()]

Promise.all(list).then(result=>{
    console.log(result)
})

// 1000ms --- ['p1', 'p2', 'p3']

for await of 是做什么的?

可遍历多个promise对象,批量调用promise

js
  // 借助 console.time() 和 console.timeEnd() 查看,得到结果:for比forEach快
  const arr = []
  for (let i = 0; i < 1000 * 10000; i++) {
    arr.push(i)
  }
  console.time()
  let a1 = 0
  for (let index = 0; index < arr.length; index++) {
    a1++
  }
  console.timeEnd()
  console.time()
  let a2 = 0
  arr.forEach(item =>{
    a2++
  })
  console.timeEnd()

要点速览(并入「遍历要点梳理」)

shell
forEach:返回值常为 undefined;无法用 break/continue。
map:返回等长新数组,适合「转换」。
reduce:累加、扁平、分组、单次扫描完成多步派生数据。
需提前结束循环:用 for / for-of;若误用 forEach,只能 try/catch + throw 模拟中断。
forEach 的回调若声明为 async,并不会顺序 await 每个元素;顺序 await for-of,并行用 Promise.all。
粗排(同量级、因引擎而异):for(索引)≈ for-of 往往优于 forEach/map;for-in 遍历数组不推荐。
可中断:仅 for / for-of(及传统 while)原生支持 break、continue。