前端开发中的常用工具函数(十):JavaScript 数组遍历
2026/8/24 1:15:55 网站建设 项目流程

目录

一、 传统 for 循环:一切的基石

1. 语法

2. 参数介绍

3. 适用业务场景

4. 注意事项

二、 forEach():朴实无华的执行者

1. 语法

2. 参数介绍

3. 适用业务场景

4. 注意事项

三、 for...of:现代异步遍历之王 (ES6)

1. 语法

2. 参数介绍

3. 适用业务场景

4. 注意事项

四、 map():数据映射器

1. 语法

2. 参数介绍

3. 适用业务场景

4. 注意事项

五、 filter():数据过滤器

1. 语法

2. 参数介绍

3. 适用业务场景

4. 注意事项

六、 reduce():全能聚合器

1. 语法

2. 参数介绍

3. 适用业务场景

4. 注意事项

七、 some() 和 every():布尔值质检员

1. 语法

2. 参数介绍

3. 适用业务场景

4. 注意事项

八、 for...in:请勿用于数组遍历

1. 语法

2. 参数介绍

3. 适用业务场景

4. 注意事项(针对数组)

总结:如何选择?

在前端开发中,我们每天都在和数组打交道。从渲染列表数据、处理接口返回的 JSON,到复杂的表单校验,几乎 80% 的业务逻辑都离不开数组遍历。

JavaScript 为我们提供了极其丰富的数组遍历方法。

一、 传统for循环:一切的基石

最原始、最硬核的遍历方式。通过控制索引来访问数组元素。

1. 语法

for (let i = 0; i < arr.length; i++) { // 执行代码 arr[i] }

2. 参数介绍

  • i:循环控制变量(索引)。
  • arr.length:数组的长度,作为循环的边界条件。

3. 适用业务场景

  • 复杂的索引计算:比如需要跳跃式遍历(i += 2),或者需要倒序遍历数组。
  • 性能极致要求:在处理超大型数组(如十万级以上数据)时,for循环的执行速度通常是最快的。

4. 注意事项

  • 易引发差一错误:需要手动维护边界条件(<还是<=极易写错),导致越界。
  • 作用域问题:早期使用var声明i会导致变量泄露到全局,现代开发必须使用let声明。
  • 优点:极其灵活,可以通过break跳出循环,通过continue跳过本次循环;可以倒序遍历。
  • 缺点:语法较为繁琐,需要手动维护索引,容易发生越界错误(差一错误)。
const arr = ['Vue', 'React', 'Angular']; for (let i = 0; i < arr.length; i++) { console.log(i, arr[i]); }

二、forEach():朴实无华的执行者

数组原生的遍历方法,它接收一个回调函数,对数组中的每个元素执行一次。

1. 语法

array.forEach(function(currentValue, index, array), thisArg)

2. 参数介绍

  • currentValue:数组中当前正在处理的元素。
  • index(可选):当前元素在数组中的索引。
  • array(可选):正在遍历的数组本身(极少用到)。
  • thisArg(可选):执行回调时用作this的值。

3. 适用业务场景

  • 纯粹的副作用操作:例如遍历 DOM 节点列表批量添加事件监听,或者遍历日志数组批量打印。
  • 修改对象数组属性:例如遍历用户列表,给每个用户对象动态添加一个isChecked: false属性。

4. 注意事项

  • 无法中途跳出:不能用breakcontinue,会直接报语法错误。
  • 不支持await:如果回调函数中有异步请求,forEach不会等待异步完成,容易导致数据不同步。
  • 无法return终止外层函数:在forEachreturn只是结束当前回调,相当于for循环里的continue
const arr = ['Vue', 'React', 'Angular']; arr.forEach((item, index, array) => { console.log(index, item); // array 就是原数组 arr });

三、for...of:现代异步遍历之王 (ES6)

for...of是 ES6 引入的语法,用于遍历可迭代对象(包括数组、Map、Set、字符串等,但不包括普通对象)。

1. 语法

for (let variable of iterable) { // 执行代码 }

2. 参数介绍

  • variable:每次迭代时,接收来自iterable的值。
  • iterable:被遍历的可迭代对象。

3. 适用业务场景

  • 包含异步操作的遍历:例如需要按顺序请求多个接口并处理结果,for...of完美支持await
  • 需要提前终止的循环:遇到特定条件需要break跳出,或continue跳过。
  • 遍历 Map 或 Set 结构:这是最直观、最推荐的方式。

4. 注意事项

  • 不遍历普通对象:普通对象默认不是可迭代的,如果需要遍历对象的键值对,请使用Object.entries(obj)配合for...of
  • 拿索引稍显繁琐:如果业务逻辑重度依赖索引,需要配合arr.entries()解构使用:for (const [index, item] of arr.entries())
    • 优点
      1. 语法最简洁直观,直接拿到值。
      2. 支持breakcontinuereturn
      3. 完美支持await:如果在async函数中,for...of会等待上一个异步操作完成再进行下一次循环。
const arr = ['Vue', 'React', 'Angular']; for (const item of arr) { console.log(item); // 直接拿到值,不需要写 arr[index] if (item === 'React') break; // ✅ 可以正常跳出 } // 如果需要索引,可以结合 entries() for (const [index, item] of arr.entries()) { console.log(index, item); }

四、map():数据映射器

map的意思是“映射”。它遍历数组,对每一项执行回调函数,并返回一个全新的数组。新数组的长度和原数组一致。

1. 语法

let newArray = array.map(function(currentValue, index, array), thisArg)

2. 参数介绍

  • currentValue:当前处理的元素。
  • index(可选):当前元素的索引。
  • array(可选):原数组。
  • thisArg(可选):执行回调时用作this的值。
  • 返回值:一个由回调函数返回值组成的新数组。

3. 适用业务场景

  • 数据结构转换:将后端返回的数据结构转换为前端组件(如 Vue/React 组件)需要的数据结构。
  • 提取属性:从对象数组中提取某个属性组成新数组,如users.map(user => user.name)

4. 注意事项

  • 必须有返回值:如果回调函数里忘记return,新数组里会全是undefined
  • 不可变性map不会改变原数组,但它返回的是浅拷贝。如果数组元素是对象,在新数组中修改对象属性,会影响原数组中的对象。
  • 优点:函数式编程风格,不修改原数组(不可变性),链式调用极其方便。
  • 缺点:如果不使用返回值,用map就是浪费内存。
const userList = [ { id: 1, name: '张三' }, { id: 2, name: '李四' } ]; // 提取所有用户的姓名组成新数组 const names = userList.map(user => user.name); console.log(names); // ['张三', '李四']

⚠️注意:很多人错把map当成forEach用,如果不需要返回新数组,请用forEachfor...of

五、filter():数据过滤器

顾名思义,filter用于过滤数组。它遍历数组的每一项,回调函数返回true的项会被保留,最终返回一个新数组

1. 语法

let newArray = array.filter(function(currentValue, index, array), thisArg)

2. 参数介绍

  • currentValue:当前处理的元素。
  • index(可选):当前元素的索引。
  • array(可选):原数组。
  • 返回值:一个通过测试的新数组。如果没有通过测试的元素,返回空数组[]

3. 适用业务场景

  • 搜索过滤:前端实现模糊搜索,过滤出包含关键字的数据。
  • 条件筛选:如从订单列表中筛选出“已付款”状态的订单。

4. 注意事项

  • 返回布尔值:回调函数内部必须返回一个条件表达式(真值或假值),如果无脑return true则相当于深拷贝。
  • 空数组处理filter不会对空数组进行检测。
    • 优点:语义清晰,专门用于筛选数据,保持原数组不变。
const numbers = [1, 2, 3, 4, 5, 6]; // 找出所有偶数 const evens = numbers.filter(num => num % 2 === 0); console.log(evens); // [2, 4, 6]

六、reduce():全能聚合器

reduce是数组方法中最强大、但也最常被初学者忽视的方法。它接收一个回调函数和一个初始值,遍历数组将每一项“累积”处理,最终返回一个单一的值。

1. 语法

let result = array.reduce(function(accumulator, currentValue, index, array), initialValue)

2. 参数介绍

  • accumulator(累计器):上一次回调函数的返回值,或者是初始值initialValue
  • currentValue:当前处理的元素。
  • index(可选):当前索引。如果提供了initialValue,从 0 开始;否则从 1 开始。
  • array(可选):原数组。
  • initialValue(强烈建议提供):作为第一次调用回调函数时的第一个参数的值。

3. 适用业务场景

  • 统计计算:计算购物车商品总价、统计数组中各个元素出现的次数。
  • 数组转对象:将[{id: 1, name: '张三'}]转换为{1: {name: '张三'}}方便快速查找。
  • 多维数组扁平化:配合空数组作为初始值,将多维数组拍平。

4. 注意事项

  • 一定要传初始值:如果不传initialValuereduce会默认使用数组的第一个元素作为初始值,并跳过第一次循环。如果数组为空且未提供初始值,会直接报错Reduce of empty array with no initial value
  • 必须返回累计器:回调函数最后必须return accumulator(或更新后的累计值),否则下一次循环拿到的就是undefined
    • 优点:功能极强,可以做求和、扁平化数组、分组、甚至实现mapfilter的组合功能。
const cart = [ { name: '键盘', price: 200 }, { name: '鼠标', price: 100 }, { name: '显示器', price: 1000 } ]; // 计算购物车总价 const totalPrice = cart.reduce((acc, item) => acc + item.price, 0); console.log(totalPrice); // 1300 // 进阶:将数组按属性分组 const grouped = cart.reduce((acc, item) => { const type = item.price > 500 ? '贵' : '便宜'; if (!acc[type]) acc[type] = []; acc[type].push(item); return acc; }, {});

七、some()every():布尔值质检员

这两个方法用于判断数组中的元素是否满足某种条件,返回值为布尔值(true/false)。它们非常高效,因为一旦能确定结果,就会提前终止遍历

  • some():只要有一项满足条件,就返回true(相当于逻辑或||)。
  • every():必须所有项都满足条件,才返回true(相当于逻辑与&&)。

1. 语法

let boolean = array.some(function(currentValue, index, array), thisArg) let boolean = array.every(function(currentValue, index, array), thisArg)

2. 参数介绍

  • currentValue:当前元素。
  • index(可选):当前索引。
  • array(可选):原数组。
  • 返回值truefalse

3. 适用业务场景

  • some(相当于逻辑或||):判断数组中是否“存在”满足条件的元素。例如:判断用户列表里是否有管理员权限(只要有一个就返回 true)。
  • every(相当于逻辑与&&):判断数组是否“全员满足”条件。例如:提交表单前校验所有必填项是否都填写完毕。
  • 适用场景:表单全量校验、权限校验(判断用户是否拥有某个权限标识)。

4. 注意事项

  • 短路特性some找到第一个符合条件的就会停止遍历并返回trueevery找到第一个不符合条件的就会停止遍历并返回false。利用这个特性可以提升性能。
  • 空数组的表现:对于空数组,some返回false,而every返回true(数学上的逻辑,因为不存在不满足条件的项)。
const scores = [80, 90, 60, 75]; // 检查是否有不及格的(只要有一个 < 60 就返回 true) const hasFail = scores.some(score => score < 60); console.log(hasFail); // true // 检查是否全员都及格(必须所有 >= 60 才返回 true) const isAllPass = scores.every(score => score >= 60); console.log(isAllPass); // false

八、for...in:请勿用于数组遍历

虽然for...in也能遍历数组,但它是为遍历对象的枚举属性而设计的。如果用在数组上,会带来隐患

1. 语法

for (let key in object) { // 执行代码 }

2. 参数介绍

  • key:对象属性的键名(在数组中是字符串形式的索引 “0”, “1”)。
  • object:被遍历的对象。

3. 适用业务场景

  • 仅用于遍历普通对象的键值对

4. 注意事项(针对数组)

  • 会遍历自定义属性:如果你给数组对象加了自定义属性arr.name = 'test'for...in会把它也遍历出来。
  • 顺序不可靠:在某些旧引擎下,遍历顺序可能不按照数组的物理顺序。
  • 性能差:因为它需要去查找原型链上的可枚举属性。
  • 结论永远不要用for...in遍历数组!
  • 缺点
    1. 遍历的是键名(字符串形式的索引),不是值。
    2. 会遍历出数组对象自定义的可枚举属性。
    3. 遍历顺序不保证(某些旧引擎下)。
const arr = ['a', 'b', 'c']; arr.customProp = 'hello'; // 给数组加了自定义属性 for (const key in arr) { console.log(key); // '0', '1', '2', 'customProp' ❌ 遍历出了索引和自定义属性! }

总结:如何选择?

面对这么多方法,记住下面这张决策树,保证你不会选错:

  1. 我想把数组变成另一种形态的新数组? ➡️map()
  2. 我想筛选出符合条件的新数组? ➡️filter()
  3. 我想把数组合并成一个值(求和、转对象)? ➡️reduce()
  4. 我只想知道数组里有没有某项满足条件? ➡️some()/every()
  5. 我想对每一项执行副作用(打印、修改外部变量),且没有异步? ➡️forEach()
  6. 循环里有async/await,或者需要break跳出? ➡️for...of
  7. 需要极其复杂的索引控制或追求极致性能? ➡️ 传统for循环

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询