关于前端面试中forEach方法的灵魂7问?

news2024/9/24 17:50:34

目录

前言

一、forEach方法支持处理异步函数吗?

二、forEach方法在循环过程中能中断吗?

三、forEach 在删除自己的元素后能重置索引吗?

四、forEach 的性能相比for循环哪个好?

五、使用 forEach 会不会改变原来的数组?

1.基本数据类型(改变不了原数组) 

2.引用类型(可以改变原数组) 

六、forEach能捕获异步函数中的错误吗?

七、forEach循环如何优化?

往期回顾 


前言

相信大家在工作或者面试中经常用foreach 方法,但你知道吗?forEach其实是for循环的一个特殊简化版,也叫增强for循环,forEach虽然是for循环的简化版本,但是并不是说forEach就比for更好用。废话不多说,下面我们一步步解开这7点疑问。

一、forEach方法支持处理异步函数吗?

首先forEach方法不支持处理异步函数,我们来详细看下如下代码

const forEachFun = async () => {
  console.log("循环开始了了");
    let queue = [3, 2, 1];
    queue.forEach(async (item) => {
        const res = await getSyncReq(item);
        console.log(`队列${res}执行完成`);
    });
    console.log("循环结束了");
};
const getSyncReq = (x) =>
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(x);
        }, 1000 * x);
    });
forEachFun();

执行结果如下

我们在程序执行过程中可以发现forEach() 无法等待异步函数完成,它将继续执行下一个项目。这意味着,如果在 forEach() 中使用异步函数,则无法保证异步任务的执行顺序。

那么如果我们想在循环中使用异步函数该怎么处理呢?

其实javascript中提供了很多函数,支持返回promise,比如map函数,filter函数,reduce函数,还有我们经常使用的for循环,都能达到预期效果,下面我们以for循环为案例,其他函数根据使用场景大同小异。

const forFun = async () => {
  console.log("循环开始了了");
    let queue = [1, 2, 3];
    for (let i = 0; i< queue.length; i++) {
      const res = await getSyncReq(queue[i]);
      console.log(`队列${res}执行完成`);
    }
    console.log("循环结束了");
};
const getSyncReq = (x) =>
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(x);
        }, 1000 * x);
    });
forFun();

 运行结果

上面的代码片段在 async 函数中使用了 await 关键字,for循环会等待 async 函数完成并返回结果,实现了整个异步请求的同步执行。

二、 forEach方法在循环过程中能中断吗?

forEach() 方法不支持使用 break 或 continue 语句来中断循环或跳过项目。如果需要跳出循环或跳过某个项目,则应使用 for 循环或其他支持 break 或 continue 语句的方法。下面我们看一段代码

const forEachBreak = async () => {
  console.log("循环开始了了");
    let queue = [3, 2, 1];
    queue.forEach(async (item) => {
      if (item === 2) { // 条件为2时中断foreach
        break;
      }
      const res = await getSyncReq(item);
      console.log(`队列${res}执行完成`);
    });
    console.log("循环结束了");
};
const getSyncReq = (x) =>
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(x);
        }, 1000 * x);
    });
forEachBreak();

执行结果

没错!直接报错 !

因此forEach不能终止打断循环,需要终止可以使用for循环实现。 

三、forEach 在删除自己的元素后能重置索引吗?

在 forEach() 中,无法控制 index 的值,它会无意识地增加,直到大于数组长度,跳出循环。因此,也不可能通过删除自身来重置索引。来看一个简单的例子:

let arrayNumbers = [1, 2, 3, 4];
arrayNumbers.forEach((item, index) => {
    console.log(item); // 1 2 3 4
    index++;
});

四、 forEach 的性能相比for循环哪个好?

先说答案,for循环的性能要高于forEach,我们用代码来测试一下

// 构建长度为20000的数组
const arrars = Array.from({ length: 20000 })

// 记录for开始时间 for循环计算
const forStart = +new Date();
for(let i = 0;i< arrars.length;i++) {
  console.log('for循环');
}
// 记录for结束时间
const forEnd = + new Date();
console.log('for循环20000次时间', forEnd - forStart); // 计算时间戳差


// 记录forEach开始时间 forEach循环计算
const forEachStart = +new Date();
arrars.forEach((item, index) => {
  console.log('forEach循环');
})
// 记录forEach结束时间
const forEachEnd = +new Date();
console.log('forEach循环20000次时间', forEachEnd - forEachStart);// 计算时间戳差

我们来看结果

得出结论:复杂的一些循环还是需要用到for循环效率更高。 

五、 使用 forEach 会不会改变原来的数组?

1.基本数据类型(改变不了原数组) 

forEach不会直接改变数组。它是用于遍历数组的一种循环结构。在每次循环的过程中,foreach会将数组中的每一个元素赋值给临时变量,然后执行循环体内的代码。这个临时变量的值改变并不会直接影响原来的数组。如下案列

const array = [1, 1, 1];

array.forEach(a => {
    a+1
});

console.log(array);//[1,1,1]

2.引用类型(可以改变原数组) 

如果临时变量是通过引用传递的,所以在循环体内对临时变量进行的修改会反映在原始数组中。如下案列

const obj = [
    {
        a : 'Tom',
        b : 22
    },
    {
        a : 'BOb',
        b : 33
    }
]

obj.forEach(aa => {
    if(aa.a === 'Tom') {
        aa.b = 23
    }
})

console.log(obj);//[{"a":"Tom","b":23},{"a":"BOb","b":33}]

那么为啥会发生引用类型可以改变原数组,但是原数组改变不了原数组呢?

那是因为基本类型在栈内存中直接存储变量与值,而引用类型的真正数据是保存在堆内存,栈内只保存了对象的变量以及对应的堆的地址,所以操作Object其实就是直接操作了原数组对象本身。

六、forEach能捕获异步函数中的错误吗?

如果异步函数在执行时抛出错误,使用 forEach() 是无法捕获该错误。这意味着即使 async 函数发生错误,forEach() 也会继续执行。

七、 forEach循环如何优化?

可以使用JS的for循环代替foreach进行双重循环,因为for循环的效率较高。另外,如果双重循环需要对数组进行操作,可以使用数组的一些高阶方法(如map、filter、reduce等)来替代循环,进一步提高效率。同时,还可以考虑将数据进行预处理,以减少循环次数。我们来看一个简单的forEach双重循环的优化。

需求:

将选中的数据中的count值更新给全量数据,思路是找出全量数据id对应的数据,然后将选中更新后的count值赋值给全量数据的count,实现全量数据的部分数据更新。

原代码

// 全部数据
let allData = [
  {
    id: 1,
    count: 1
  },
  {
    id: 2,
    count: 2
  },
  {
    id: 3,
    count: 3
  },
  {
    id: 4,
    count: 4
  },
]

// 选中数据
let selectData = [
  {
    id: 2,
    count: 22
  },
  {
    id: 4,
    count: 444
  },
]
// 找出选中数据并且更新count值
allData.forEach(item => {
  selectData.forEach(list => {
    if (item.id === list.id) {
      item.count = list.count;
    }
  })
})
console.log(allData)

这个双重forEach如何优化呢?我们可以使用for-of循环或者Array.prototype.map()等其他更高效的循环方法。

相比于forEach,for-of循环、Array.prototype.map()等方法会有更好的性能表现。因为它们直接迭代数组的索引值,而不需要创建新的函数上下文。示例代码如下所示:

双map优化

// 全部数据
let allData = [
  {
    id: 1,
    count: 1
  },
  {
    id: 2,
    count: 2
  },
  {
    id: 3,
    count: 3
  },
  {
    id: 4,
    count: 4
  },
]
 
// 选中数据
let selectData = [
  {
    id: 2,
    count: 22
  },
  {
    id: 4,
    count: 444
  },
]
 
const newSelect = Object.fromEntries(
  selectData.map(({ id, count }) => [id, { count }])
)
const newAllData = allData.map(s =>  {
  s.id in newSelect && (s.count = newSelect[s.id].count);
  return s;
})
console.log(newAllData)

for-of和map结合循环优化 

// 全部数据
let allData = [
  {
    id: 1,
    count: 1
  },
  {
    id: 2,
    count: 2
  },
  {
    id: 3,
    count: 3
  },
  {
    id: 4,
    count: 4
  },
]
 
// 选中数据
let selectData = [
  {
    id: 2,
    count: 22
  },
  {
    id: 4,
    count: 444
  },
]

for (let item of allData) {
  item.id in newSelect && (item.count = newSelect[item.id].count);
}
console.log(allData)

我们用map构造新的数组,id为key值,value值为选中项,然后通过一层map循环或者for-of循环实现对部分全量数据的更新,源代码外层的forEach不必要,只是为了判断是否交集可以做一个映射。 

今天就介绍到这里啦!是不是对forEach函数有了进一步的认识?希望能在面试或工作中帮到大家!有不足之处,大家补充,如果文章对你有所帮助,❤️关注+点赞❤️鼓励一下博主会持续更新。。。。

我的个人博客:https://code-nav.top

往期回顾 

Javascript 地狱级的if else / switch case该如何优化?

 css实现元素居中的6种方法 

Angular8升级至Angular13遇到的问题

前端vscode必备插件(强烈推荐)

Webpack性能优化

vite构建如何兼容低版本浏览器

前端性能优化9大策略(面试一网打尽)!

vue3.x使用prerender-spa-plugin预渲染达到SEO优化

 vite构建打包性能优化

 vue3.x使用prerender-spa-plugin预渲染达到SEO优化

 ES6实用的技巧和方法有哪些?

 css超出部分显示省略号

vue3使用i18n 实现国际化

vue3中使用prismjs或者highlight.js实现代码高亮

什么是 XSS 攻击?什么是 CSRF?什么是点击劫持?如何防御

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1397438.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

智能驾驶新浪潮:SSD与UFS存储技术如何破浪前行?-UFS篇

如果说SSD是赛道上的超级跑车&#xff0c;那UFS更像是专为智能汽车定制的高性能轻量化赛车。UFS采用串行接口技术&#xff0c;像是闪电侠一样&#xff0c;将数据传输的速度推向新高&#xff0c;大幅缩短了系统启动时间和应用程序加载时间&#xff0c;这对追求即时反应的ADAS系统…

【从零开始学习Java重要知识 | 第三篇】暴打ReentrantLock底层源码

目录 前言&#xff1a; 前置知识&#xff1a; 什么是公平锁与非公平锁&#xff1f; 尝试自己构造一把锁&#xff1a; ReentrantLock源码&#xff1a; 加锁&#xff1a; 解锁&#xff1a; 总结&#xff1a; 前言&#xff1a; 在并发编程中&#xff0c;线程安全是一个重…

【轮式平衡机器人】——软硬件配置/准备

本系列以轮式平衡移动机器人为例&#xff0c;将使用基于模型设计&#xff08;MBD&#xff09;方法进行介绍&#xff0c;涉及基础硬件、软件、控制算法等多方面内容&#xff0c;结合MATLAB/Simulink的强大仿真能力和代码生成能力辅助设计&#xff01;在此过程中可以系统了解开发…

SpringBoot+dynamic-datasource实现多数据源(msyql、sqlserver、postgresql)手动切换

场景 SpringBootMybatisPlusdynamic-datasources实现连接Postgresql和mysql多数据源&#xff1a; SpringBootMybatisPlusdynamic-datasources实现连接Postgresql和mysql多数据源-CSDN博客 上面实现通过注解和配置文件的方式去进行多数据源操作。 如果业务需求&#xff0c;比…

Tensorflow2.0笔记 - 不使用layer方式,简单的MNIST训练

本笔记不使用layer相关API&#xff0c;搭建一个三层的神经网络来训练MNIST数据集。 前向传播和梯度更新都使用最基础的tensorflow API来做。 import tensorflow as tf from tensorflow import keras from tensorflow.keras import datasets import numpy as npdef load_mnist(…

arthas(阿尔萨斯)日常java代码调优使用命令

官方项目文档&#xff1a;https://gitee.com/arthas/arthas &#xff08;最权威的教学还是得官网&#xff0c;这里仅作简单记录&#xff09; 1&#xff1a;启动 java -jar arthas-boot.jar 2&#xff1a;查看cpu占用排名前三 thread -3 3&#xff1a;查看指定id thread 203 4:查…

ui 开发 剪辑等工具集成网站

这里给大家推荐一个工具集成网站,总体来说还是挺不错的 菜鸟工具 - 不止于工具

mysql中DATE_FORMAT() 函数详解

mysql中DATE_FORMAT() 函数详解 一. 说明 在 MySQL 中&#xff0c;DATE_FORMAT() 函数用于将日期/时间类型的值按照指定的格式进行格式化输出。它的一般语法如下&#xff1a; DATE_FORMAT(date, format)其中&#xff0c;date 参数是要被格式化的日期/时间值&#xff0c;form…

C++03:条件与分支语句

2024年1月14日 内容来自The Cherno&#xff1a;C系列 2024年1月17日 更新内容整理自&#xff1a; 南京大学 陈佳俊 郑涛 《程序设计教程 用C语言编程》 --------------------------------------------------------------------------------------------------------------…

Java和SpringBoot学习路线图

看了一下油管博主Amigoscode的相关视频&#xff0c;提到了Java和SpringBoot的学习路线&#xff0c;相关视频地址为&#xff1a; How To Master Java - Java for Beginners RoadmapSpring Boot Roadmap - How To Master Spring Boot 如下图所示&#xff1a; 当然关于Java和Spr…

【声光语音告警】小机房-动环系统与服务器监控二合一告警方案

目前场景及存在的问题 目前有很多小规模机房&#xff0c;服务器数量不多&#xff0c;机房面积也较小&#xff0c;例如医院、车站、博物馆、学校、工厂等环境。机房虽小&#xff0c;但仍然需要进行服务器性能监控以及机房动力环境监控&#xff0c;例如漏水、温湿度、烟感、电压…

ChatGPT企业版跟个人版有什么区别?

ChatGPT企业版&#xff08;ChatGPT Enterprise&#xff09;除了有和个人版GPT一样的功能外&#xff0c;企业版还可提供企业级的安全和隐私、以及数据分析功能。 订阅实用可以看下这篇文章&#xff1a; ChatGPT企业版的区别是&#xff0c;企业版允许客户输入公司的数据&#xf…

6.4.2转换文件

6.4.2转换文件 利用Swf2VideoConverter2可以很方便地将Flash动画(*.swf)转换为其它的视频格式。 1&#xff0e;单击“添加”按钮&#xff0c;在弹出的下拉菜单中选择“添加文件”&#xff0c;在弹出的“Open Swf Files(打开Swf文件)”窗口中选择swf文件(如&#xff1a;那些花…

拉索回归(Lasso Regression)的原理是什么?

拉索回归&#xff08;Lasso Regression&#xff09;&#xff0c;全称Least Absolute Shrinkage and Selection Operator回归&#xff0c;是一种线性回归的改进方法&#xff0c;主要用于数据分析和特征选择。其核心原理是在传统的线性回归损失函数中加入了一个L1正则化项&#x…

Javaweb超详细实现模拟支付宝扫码支付

1.普通方式创建Javaweb项目 首先创建Java项目 2.创建好的项目添加web框架支持 如图选择确定 在项目结构中配置有关信息 右键创建classes文件夹与lib文件夹 如图 此处找到刚才的项目的classes路径设置 在依赖中设置lib路径的设置 找到刚才的lib路径 选择此选项 结束项目结构中模…

web蓝桥杯真题--10、灯的颜色变化

介绍 我们经常会看到各种颜色的灯光&#xff0c;本题我们将实现一个颜色会变化的灯的效果。 准备 开始答题前&#xff0c;需要先打开本题的项目代码文件夹&#xff0c;目录结构如下&#xff1a; ├── effect.gif ├── images │ ├── greenlight.svg │ ├── l…

初识SpringBoot

SpringBoot以约定大于配置的核心思想,默认帮我们进行了很多设置,简单来说就是SpringBoot其实不是什么新的框架&#xff0c;它默认配置了很多框架的使用方式&#xff0c;就像maven整合了所有的jar包&#xff0c;spring boot整合了所有的框架 。 创建的包一定要在项目主程序入口…

MATLAB - 计算机械臂关节扭矩以平衡末端力和力矩

系列文章目录 前言 产生力矩以平衡作用在平面机器人末端执行器体上的端点力。要使用各种方法计算关节力矩&#xff0c;请使用刚体树机器人模型的几何雅各比&#xff08;geometricJacobian&#xff09;和反动力学&#xff08;inverseDynamics&#xff09;对象函数。 一、初始化…

JavaScript的代码执行顺序

&#xff08;1&#xff09;. js的执行顺序&#xff0c;先同步后异步 &#xff08;2&#xff09;. 异步中任务队列的执行顺序&#xff1a; 先微任务microtask队列&#xff0c;再宏任务macrotask队列 注意&#xff0c;按顺序从上到下时&#xff0c;没有轮到执行的任务会进入相应…

PowerScale重磅升级,加速迈进AI时代

2024开年 给大伙报告一则好消息 Dell非结构化数据存储的扛把子 PowerScale迎来重大升级 第二代PowerScale全闪存系统 即将闪亮登场 此次升级主要涉及硬件、软件及与NVIDIA的合作关系三个方面&#xff0c;升级后的PowerScale有望成为第一个通过 NVIDIA DGX SuperPOD验证的以…