在 JavaScript 中,forEach、filter、map 是数组(Array)原型上非常常用的高阶函数(Higher-order Functions),它们都用于遍历数组,但目的、返回值和使用场景各不相同。下面详细说明它们的作用、区别和典型应用场景:
1. forEach
✅ 作用:
对数组的每个元素执行一次回调函数,常用于执行副作用操作(如打印、修改外部变量、发起请求等)。
🔁 返回值:
undefined(不返回新数组,也不改变原数组)
语法:
arr.forEach((element, index, array) => {
/* ... */
});
应用场景:
- 遍历数组并打印日志
- 向服务器批量发送请求
- 修改外部状态(如累加器、DOM 操作)
❌ 不能:
- 中断循环(没有
break) - 返回新数组
示例:
const numbers = [1, 2, 3];
numbers.forEach((n) => console.log(n * 2)); // 输出: 2, 4, 6
2. map
✅ 作用:
对数组中每个元素调用回调函数,并返回一个新数组,新数组的每个元素是回调函数的返回值。
🔁 返回值:
新数组(长度与原数组相同)
语法:
const newArray = arr.map((element, index, array) => {
return transformedValue;
});
应用场景:
- 数据转换(如把 ID 数组转为对象)
- 格式化数据(如价格加单位、日期格式化)
- React/Vue 中渲染列表前的数据映射
示例:
const names = ["alice", "bob"];
const upperNames = names.map((name) => name.toUpperCase());
// 结果: ['ALICE', 'BOb']
3. filter
✅ 作用:
筛选数组中满足条件的元素,返回一个新数组。
🔁 返回值:
新数组(包含所有使回调返回 true 的元素)
语法:
const filteredArray = arr.filter((element, index, array) => {
return booleanCondition;
});
应用场景:
- 搜索/过滤(如“只显示已激活用户”)
- 去除无效值(如过滤掉
null、0、空字符串) - 权限控制(如“只显示当前角色可访问的菜单”)
示例:
const scores = [85, 92, 78, 96];
const passed = scores.filter((score) => score >= 80);
// 结果: [85, 92, 96]
核心区别总结
| 方法 | 是否返回新数组 | 是否修改原数组 | 主要用途 | 能否链式调用 |
|---|---|---|---|---|
forEach | ❌(返回 undefined) | ❌ | 执行副作用(如打印、请求) | ❌ |
map | ✅ | ❌ | 转换每个元素 | ✅ |
filter | ✅ | ❌ | 筛选符合条件的元素 | ✅ |
✅ 这三个方法都不会修改原数组(除非你在回调中显式修改引用类型的内容)。
🔄 链式调用示例(常见组合)
const users = [
{ name: "Alice", age: 25, active: true },
{ name: "Bob", age: 30, active: false },
{ name: "Charlie", age: 35, active: true },
];
// 获取所有活跃用户的姓名(大写)
const activeNames = users
.filter((user) => user.active)
.map((user) => user.name.toUpperCase());
// 结果: ['ALICE', 'CHARLIE']
这种链式写法清晰、函数式、无副作用,是现代 JS 开发的推荐方式。
⚠ 注意事项
- 不要在
map中做副作用操作(如console.log),这违背其“纯函数”设计初衷。 - 如果只是想遍历而不返回结果,用
forEach;如果要生成新数据,优先用map/filter。 - 性能上,
for循环更快,但在可读性和维护性上,这些高阶函数更优(除非性能敏感场景)。
✅ 一句话记忆:
forEach:做事(执行操作)map:变形(每个元素变个样)filter:挑人(留下符合条件的)