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

目录

前言

一、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时中断foreachbreak;}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 4index++;
});

四、 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如何优化呢?我们可以这样优化

// 全部数据
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 map = Object.fromEntries(selectData.map(({ id, count }) => [id, { count }])
)
allData.forEach(s => s.id in map && (s.count = map[s.id].count))
console.log(allData)

我们用map构造新的数组,id为key值,value值为选中项,然后通过一层forEach循环实现对部分全量数据的更新,源代码外层的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.mzph.cn/news/626369.shtml

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

相关文章

xshell:关于ssh用户身份验证不能选择password的解决方法

接下来我将告诉大家如何进行修改让其能够进行密码登录 我使用的软件是VM VirtualBox管理器 进行用户名密码登录后 输入 cd /etc/ 切换到etc目录下 cd /etc/ 切换到etc目录后输入ls ls 切换到ssh目录下 cd ssh 进入文件 sshd_config vi sshd_config 找到指定部分进行修改 如何…

多级缓存架构(三)OpenResty Lua缓存

文章目录 一、nginx服务二、OpenResty服务1. 服务块定义2. 配置修改3. Lua程序编写4. 总结 三、运行四、测试五、高可用集群1. openresty2. tomcat 通过本文章&#xff0c;可以完成多级缓存架构中的Lua缓存。 一、nginx服务 在docker/docker-compose.yml中添加nginx服务块。…

评估文字识别准确性的方法与流程

随着信息技术的发展&#xff0c;文字识别技术在各个领域得到了广泛的应用。然而&#xff0c;在实际应用中&#xff0c;如何评估文字识别的准确性&#xff0c;一直是相关领域的一个难题。本文将介绍几种常用的文字识别准确性评估方法&#xff0c;以期为相关领域的研究提供参考。…

使用vite框架封装vue3插件,发布到npm

目录 一、vue环境搭建 1、创建App.vue 2、修改main.ts 3、修改vite.config.ts 二、插件配置 1、创建插件 2、开发调试 3、打包配置 4、package.json文件配置 上一篇文章讲述使用vite《如何使用vite框架封装一个js库&#xff0c;并发布npm包》封装js库&#xff0c;本文将…

Jmeter后置处理器——JSON提取器

目录 1、简介 2、使用步骤 1&#xff09;添加线程组 2&#xff09;添加http请求 3&#xff09; 添加JSON提取器 1、简介 JSON是一种简单的数据交换格式&#xff0c;允许互联网应用程序快速传输数据。JSON提取器可以从JSON格式响应数据中提取数据、简化从JSON原始数据中提取特定…

Java学习——Junit单元测试

​​ Junit&#xff1a;事实上的标准单元测试框架 使用Junit&#xff1a;只需要使用 TestCase 和 Assert http://t.csdnimg.cn/hgMFJ

Linux网络编程---IP 地址格式转换函数

Linux网络编程—IP 地址格式转换函数 我们更容易阅读的IP地址是以点分十进制表示的&#xff0c;例如&#xff1a;192.168.5.10 &#xff0c;这是一种字符串的形式&#xff0c;但是计算器所需要的IP地址是以二进制进行表示&#xff0c;这便需要我们在点分十进制字符串和二进制地…

java版直播商城平台规划及常见的营销模式 电商源码/小程序/三级分销+商城 免 费 搭 建

鸿鹄云商 B2B2C产品概述 【B2B2C平台】&#xff0c;以传统电商行业为基石&#xff0c;鸿鹄云商支持“商家入驻平台自营”多运营模式&#xff0c;积极打造“全新市场&#xff0c;全新 模式”企业级B2B2C电商平台&#xff0c;致力干助力各行/互联网创业腾飞并获取更多的收益。从消…

【现代密码学】笔记6--伪随机对象的理论构造《introduction to modern cryphtography》

【现代密码学】笔记6--伪随机对象的理论构造《introduction to modern cryphtography》 写在最前面6 伪随机对象的理论构造 写在最前面 主要在 哈工大密码学课程 张宇老师课件 的基础上学习记录笔记。 内容补充&#xff1a;骆婷老师的PPT 《introduction to modern cryphtogr…

Qt/C++中英输入法/嵌入式输入法/小数字面板/简繁切换/特殊字符/支持Qt456

一、前言 在嵌入式板子上由于没有系统层面的输入法支持&#xff0c;所以都绕不开一个问题&#xff0c;那就是在需要输入的UI软件中&#xff0c;必须提供一个输入法来进行输入&#xff0c;大概从Qt5.7开始官方提供了输入法的源码&#xff0c;作为插件的形式加入到Qt中&#xff…

网络广播号角喇叭在智能工地施工现场的应用,以及网络广播在公共广播中的实际作用。

网络号角喇叭在智能工地施工现场的应用&#xff0c;以及网络广播在公共广播中的实际作用。 SV-7044村村通ip网络通信广播号角喇叭&#xff0c;网络音箱&#xff0c;网络音柱是一种公共广播技术&#xff0c;主要应用于公共场所&#xff0c;如公交、商场、大型活动场所等。可以用…

visual studio的安装及scanf报错的解决

visual studio是一款很不错的c语言编译器 下载地址&#xff1a;官网 点击后跳转到以下界面 下滑后点击下载Vasual Sutdio&#xff0c;选择社区版即可 选择位置存放下载文件后&#xff0c;即可开始安装 安装时会稍微等一小会儿。然后会弹出这个窗口&#xff0c;我们选择安装位…

无需编程,简单易上手的家具小程序搭建方法分享

想要开设一家家具店的小程序吗&#xff1f;现在&#xff0c;我将为大家介绍如何使用乔拓云平台搭建一个家具小程序&#xff0c;帮助您方便快捷地开展线上家具销售业务。 第一步&#xff0c;登录乔拓云平台进入商城后台管理页面。 第二步&#xff0c;在乔拓云平台的后台管理页面…

Vulnhub-Raven-1

一、信息收集 端口扫描 PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 6.7p1 Debian 5deb8u4 (protocol 2.0) | ssh-hostkey: | 1024 26:81:c1:f3:5e:01:ef:93:49:3d:91:1e:ae:8b:3c:fc (DSA) |_ 256 0e:85:71:a8:a2:c3:08:69:9c:91:c0:3f:84:18:df:…

多线程——CAS

什么是CAS CAS的全称&#xff1a;Compare and swap&#xff0c;字面意思就是&#xff1a;“比较并交换”&#xff0c;一个CAS涉及到以下操作&#xff1a; 假设内存中的原数据V&#xff0c;旧的预期值A&#xff0c;需要修改的新值B 1.比较A与V是否相等&#xff08;比较&#xf…

antd pro项目部署到gitpage白屏

先总结一下如何部署项目到gitpage 1.新建分支gh-pages 2.把打包好的文件放在这个分支下 3. 之前打开一直白屏&#xff0c;有很多坑 第一个&#xff0c;import { getIntl } from umijs/max;这个引入要&#xff0c;不能是./引入的 第二个&#xff0c;新建一个config.prod.t…

盘点2023年信息系统故障

安全生产&#xff0c;人人有责。每年信息系统安全事件层出不穷&#xff0c;作为一线运维人员对这些生产安全故障当抱有敬畏之心&#xff0c;并从中总结经验教训&#xff0c;分析原因&#xff0c;不能简单的调侃为开猿节流、降本增笑的结果。本文简要盘点2023年发生的主要信息系…

Java NIO (一)简介(备份)

1 NIO简介 在1.4版本之前&#xff0c;Java NIO类库是阻塞IO&#xff0c;从1.4版本开始&#xff0c;引进了新的异步IO库&#xff0c;被称为Java New IO类库&#xff0c;简称为Java NIO。New IO类库的目的 就是要让Java支持非阻塞IO。 Java NIO类库包含三个核心组件&#xff1a; …

Shell基本操作(2)

文件显示与编辑 连接并显示文件内容 cat cat[options] file... options -n加上行号 -s将连续两行以上的空白行替换为一行如果file不止一个文件&#xff0c;则会将它们连接起来如果想一次只看一页&#xff0c;可以使用more或者less命令 过滤文件内容grep grep命令可以查找拥…

【STM32CubeMX串口通信详解】USART1 -- DMA发送 + DMA空闲中断 接收不定长数据

文章目录&#xff1a; 前言 一、准备工作 1、接线 2、新建工程 二、CubeMX的配置 1、USART1 配置 异步通信 2、通信协议参数 3、打开DMA发送、接收 三、发送操作、代码解释 四、printf 重定向到USART1 五、接收代码的编写 1、定义一个结构体变量&a…