JavaScript 中 forEach 和 map 的区别是什么?

比较 JavaScript 中的 forEach 和 map 方法在使用场景、返回值及对原数组的影响上的区别。

JavaScript 中等 数组遍历 Array 函数式编程

forEachmap 都是 JavaScript 中用于遍历数组的方法,但它们在使用方式、返回值、是否修改原数组等方面有显著区别。以下是两者的详细对比:

🛠 1. 核心区别

特性 map forEach
返回值 ✅ 返回新数组(元素为回调返回值) ❌ 返回 undefined
修改原数组 ❌ 不修改原数组 ⚠️ 可通过索引修改原数组元素
链式调用 ✅ 可链式调用(如结合 filter ❌ 不支持链式调用
性能 🔥 通常更快(约快 70%) ⏱ 较慢

📝 2. 使用场景

  • map 适用场景
    需基于原数组生成新数组(如数据转换、提取属性),或需链式操作(如 arr.map(...).filter(...))。
    const numbers = [1, 2, 3];
    const squares = numbers.map(num => num * num); // [1, 4, 9]
    
  • forEach 适用场景
    仅需遍历数组执行操作(如打印、更新外部变量),或需通过索引修改原数组(非整个元素替换)。
    const arr = [10, 20, 30];
    arr.forEach((item, index) => {
      if (item === 20) arr[index] = 200; // 直接修改原数组:arr 变为 [10, 200, 30]
    });
    

⚠️ 3. 修改原数组的注意事项

  • 基本数据类型
    forEach 中直接修改 item 无效(因 item 是值拷贝),必须通过 arr[index] 修改:
    const nums = [1, 2, 3];
    nums.forEach((num, index, arr) => {
      num = num * 10;       // ❌ 无效
      arr[index] = num * 10; // ✅ 有效:nums 变为 [10, 20, 30]
    });
    
  • 引用数据类型
    修改 item 的属性会影响原数组(因 item 是地址拷贝),但替换整个 item 无效
    const users = [{ name: "Alice" }, { name: "Bob" }];
    users.forEach((user, index, arr) => {
      user.name = "Carol";    // ✅ 修改属性:原数组同步更改
      user = { name: "Dave" }; // ❌ 替换整个对象无效
      arr[index] = { name: "Dave" }; // ✅ 需通过索引替换
    });
    

🔧 4. 代码示例对比

// 示例 1:生成新数组 vs 仅遍历
const arr = [1, 2, 3];
const mapped = arr.map(x => x * 2); // [2, 4, 6](新数组)
arr.forEach(x => console.log(x));    // 打印 1, 2, 3(无返回值)

// 示例 2:链式调用
const result = arr.map(x => x + 1)
  .filter(x => x > 2); // [3, 4](链式操作)

// 示例 3:修改原数组
const items = [{ count: 1 }, { count: 2 }];
items.forEach(item => item.count++); // ✅ 原数组变为 [{count:2}, {count:3}]

💎 总结

方法选择 原因
需生成新数组 → map 返回新数组且不污染原数据,适合函数式编程
需修改原数组 → forEach 可通过索引直接修改,但注意数据类型限制
仅遍历不修改 → forEach 语义更清晰,避免无意义的新数组内存开销