浅谈 map 和 forEach 的区别(是否原始数组?❌❌❌)

这个问题你的第一反应是什么?

很多人都会说map不会改变原数组,而forEach可以改变原数组。❌

看完这篇文章,相信你对这个问题会有一个新的理解!

forEach

先来看看TC39 对forEach的介绍:

注意框起来的段落:

  • forEach 不会直接更改被调用的对象,但调用 callbackfn 时可能会更改对象。
  • forEach 所处理的元素范围是在首次调用 callbackfn 之前设置的。调用 forEach 开始后追加到数组中的元素将不会被 callbackfn 访问。如果数组中的现有元素被更改,传递给 callbackfn 的值将是 forEach 访问这些元素时的值。

给出如下示例验证:

被遍历数据:

js 复制代码
const numberArray = [1, 2, 3, 4];
const objectArray = [{ age: 15 }, { name: 24 }, { name: 30 }];
  • 修改原数组:
    • 对于原始类型,需要通过使用原数组+索引的方式,这种方式无论在什么情况都是可以改变值的,但不能直接赋值
    • 对于引用类型,可以直接修改其属性
js 复制代码
numberArray.forEach((value, index, array) => {
  array[index] *= 2;
}); //[2,4,6,8]

objectArray.forEach((value, index, array) => {
  value.age += 2;
});[{ age: 17 }, { age: 26 }, { age: 32 }]
  • 遍历范围 及 当前遍历的值:

    • 调用次数在调用回调前就确定了
    js 复制代码
      numberArray.forEach((value, index) => {
      numberArray.push(index);
      }); //[1,2,3,4,0,1,2,3]
    • 每次使用的value都是调用时的值
    js 复制代码
    numberArray.forEach((value, index) => {
    //遍历第一个元素时将第二个元素修改为-1
    if (index === 0) {
      numberArray[1] = -1;
    }
    //遍历第二个元素时输出
    if (index === 1) {
      console.log(value); //-1
    }
    });

除了这些之外:

  • forEach回调的返回值永远是undefined
  • forEach每次的循环可以使用return跳过

map

再来看看TC39对map的描述:

完全一样,这里其实就已经推翻了之前所说的:map不会改变原数组,而forEach可以改变原数组。

因此上面的对于forEach的示例,在使用map时效果一样;

难道map和forEach就没有区别了?

当然不是:

  • 区别一:map有返回值,返回的是每次调用回调的返回值组成的新数组;
  • 区别二:map的return调用时,返回值会被收集;

深度对比

接下来将给出map和forEach底层算法的步骤,并标注区别;

重点关注map的逻辑:在map执行回调之前,就会创建一个与原数组长度相等的数组,然后每次回调的返回值将加入到新数组中 ,这也是为什么map不支持跳过循环的原因。

总结

在我们了解了其工作原理和底层算法设计之后,就能总结出两者的区别了

相同

  • 对于修改原数组:map和forEach都可以修改原数组,因为我们可以访问到原始数组,那么对于原始类型的修改就可以利用array[index]进行赋值。
  • 对于循环的跳出
    • map和forEach都不能完全中断循环,即不存在for循环中调用的break的效果,但是你可以使用try catch并在回调中手动抛出异常就可以中断循环了。
    • 使用return就可以达到在for循环中调用continue的效果,
      • 但是对于map来说,一定要注意return的值将被收集到新数组。

不同

  • 对于返回值:
    • map存在返回值,其返回值为一个与原数组等长的新数组,且其中每个元素为每次调用回调的返回值
    • forEach的返回值永远是undefined
  • 对于设计目的:
    • forEach往往使用在仅希望遍历数组,利用数组的元素来完成某些工作
    • map往往是为了根据原始数组的元素生成一个新的数组
相关推荐
Amos_Web6 小时前
Rspack 源码解析(十四):Resolver 与 NormalModuleFactory
前端·rust·源码
PC2005_cloud6 小时前
Spring Boot 事务回滚方法
前端·后端
颜进强6 小时前
装了个 AI Skill 却查不了数据?一篇讲透 Skill 调用接口的三种方式(scripts / CLI / MCP)
前端·后端·ai编程
无责任此方_修行中6 小时前
插件+1:MiaoMint —— 类 RayCast 的标签管理工具
前端·javascript·vibecoding
IT_陈寒6 小时前
Redis误删数据后的血泪教训:我竟然这样找回来了
前端·人工智能·后端
去伪存真6 小时前
从零到一开发一个英语情景教学Agent
前端·人工智能
创新技术阁6 小时前
FastapiAdmin插件介绍
前端·后端·fastapi
白雾茫茫丶6 小时前
VibeCoding 一套 Admin 系统,五种技术栈实现
前端·ai编程·vibecoding
Yeyu6 小时前
不靠 libaums自己如何在 Android 上用 USB 协议把 U 盘读出来
前端
夏天要喝冰可乐6 小时前
Trae 每天自动签到:Serverless 定时任务完整复盘
前端·python