浅谈 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往往是为了根据原始数组的元素生成一个新的数组
相关推荐
Csvn4 小时前
Monorepo 迁移血泪史:从 Multi-Repo 到 Turborepo,这 3 个坑我帮你踩完了
前端
星栈4 小时前
Dioxus 多页面怎么做:`dioxus-router`、嵌套路由、`Outlet` 和页面组织,一篇给你讲顺
前端·rust·前端框架
用户987409238874 小时前
用 Remotion + edge-tts 打造中文教学视频全自动流水线
前端
风骏时光牛马4 小时前
Less前端工程化实战:变量混合器与项目样式分层落地
前端
假如让我当三天老蒯4 小时前
Options API(选项式 API) 和 Composition API(组合式 API)
前端·vue.js·面试
SameX5 小时前
iOS 独立开发实践:用 MapKit + 像素渲染实现 Citywalk 轨迹地图 App「雁过留痕」
前端
_柳青杨5 小时前
一文吃透 Node.js 事件循环:从原理到 Node 20+ 重大变更
javascript·后端
skyey5 小时前
页面加载时,深色模式闪白的问题解决
前端
IT_陈寒5 小时前
Java 并行流把我坑惨了,这6小时加班值了
前端·人工智能·后端
anOnion15 小时前
构建无障碍组件之Menu Button pattern
前端·html·交互设计