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

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

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

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

forEach

先来看看TC39forEach的介绍:

注意框起来的段落:

  • 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时效果一样;

难道mapforEach就没有区别了?

当然不是:

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

深度对比

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

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

总结

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

相同

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

不同

  • 对于返回值:
    • map存在返回值,其返回值为一个与原数组等长的新数组,且其中每个元素为每次调用回调的返回值
    • forEach的返回值永远是undefined
  • 对于设计目的:
    • forEach往往使用在仅希望遍历数组,利用数组的元素来完成某些工作
    • map往往是为了根据原始数组的元素生成一个新的数组
相关推荐
负责的蛋挞43 分钟前
异步HttpModule的实现方式
java·服务器·前端
丹宇码农3 小时前
把 HLS 字幕玩出花:zwPlayer 如何让 M3U8 视频支持全文搜索、翻译与码率自适应
前端·javascript·音视频·hls·视频播放器
2501_943782353 小时前
【共创季稿事节】猜数字游戏:二分法思维与交互式反馈
前端·游戏·microsoft·harmonyos·鸿蒙·鸿蒙系统
GV191rLvq4 小时前
基于Socket实现的最简单的Web服务器【ASP.NET原理分析】
服务器·前端·asp.net
吠品4 小时前
LangChain 里 tool_call_id 为空?一次 MCP 工具集成的排查记录
前端
微信开发api-视频号协议4 小时前
企业微信二次开发中的文件系统设计:媒体资源、临时文件与业务附件
前端·微信·企业微信·媒体·ipad·微信开放平台
柒和远方4 小时前
Phase 7.4 学习博客:为什么多 API 项目需要 Swagger / OpenAPI
前端·后端·架构
张龙6874 小时前
拼多多开放平台对接踩坑实录:从 CLIENT_ID 配置到 MD5 签名算法的完整填坑指南
前端
GuWenyue4 小时前
提示词彻底过时?一套上下文工程方案,3步让LLM落地生产,代码直接复用
前端·javascript·人工智能