浅谈 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往往是为了根据原始数组的元素生成一个新的数组
相关推荐
come112346 分钟前
Vue 响应式数据传递:ref、reactive 与 Provide/Inject 完全指南
前端·javascript·vue.js
前端风云志27 分钟前
TypeScript结构化类型初探
javascript
musk121244 分钟前
electron 打包太大 试试 tauri , tauri 安装打包demo
前端·electron·tauri
翻滚吧键盘1 小时前
js代码09
开发语言·javascript·ecmascript
万少2 小时前
第五款 HarmonyOS 上架作品 奇趣故事匣 来了
前端·harmonyos·客户端
OpenGL2 小时前
Android targetSdkVersion升级至35(Android15)相关问题
前端
rzl022 小时前
java web5(黑马)
java·开发语言·前端
Amy.Wang2 小时前
前端如何实现电子签名
前端·javascript·html5
海天胜景2 小时前
vue3 el-table 行筛选 设置为单选
javascript·vue.js·elementui
今天又在摸鱼2 小时前
Vue3-组件化-Vue核心思想之一
前端·javascript·vue.js