关于前端面试中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如何优化呢?我们可以使用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?什么是点击劫持?如何防御
相关文章:

关于前端面试中forEach方法的灵魂7问?
目录 前言 一、forEach方法支持处理异步函数吗? 二、forEach方法在循环过程中能中断吗? 三、forEach 在删除自己的元素后能重置索引吗? 四、forEach 的性能相比for循环哪个好? 五、使用 forEach 会不会改变原来的数组&#…...

AI小程序添加深度合成类目解决办法
基于文言一心和gpt等大模型做了一个ai助理小程序,在提交“一点AI助理”小程序时,审核如下: 失败原因1 审核失败原因 你好,你的小程序涉及提供提供文本深度合成技术 (如: AI问答) 等相关服务,请补充选择:深度…...

C/C++ BM6判断链表中是否有环
文章目录 前言题目解决方案一1.1 思路阐述1.2 源码 解决方案二2.1 思路阐述2.2 源码 总结 前言 做了一堆单链表单指针的题目,这次是个双指针题,这里双指针的作用非常明显。 题目 判断给定的链表中是否有环。如果有环则返回true,否则返回fal…...

【Java 设计模式】结构型之适配器模式
文章目录 1. 定义2. 应用场景3. 代码实现结语 适配器模式(Adapter Pattern)是一种结构型设计模式,用于将一个类的接口转换成客户端期望的另一个接口。这种模式使得原本由于接口不兼容而不能一起工作的类可以一起工作。在本文中,我…...

使用函数计算,数禾如何实现高效的数据处理?
作者:邱鑫鑫,王彬,牟柏旭 公司背景和业务 数禾科技以大数据和技术为驱动,为金融机构提供高效的智能零售金融解决方案,服务银行、信托、消费金融公司、保险、小贷公司等持牌金融机构,业务涵盖消费信贷、小…...

卷积和滤波对图像操作的区别
目录 问题引入 解释 卷积 滤波 问题引入 卷积和滤波是很相似的,都是利用了卷积核进行操作 那么他们之间有什么区别呢? 卷积:会影响原图大小 滤波:不会影响原图大小 解释 卷积 我们用这样一段代码来看 import torch.nn as …...
李沐深度学习-线性回归从零开始
# 核心Tensor,autograd import torch from IPython import display import numpy as np import random from matplotlib import pyplot as pltimport syssys.path.append(路径) from d2lzh_pytorch import * backward()函数:一次小批量执行完在进行反向传播 线性回归…...

CentOS 8.5 安装图解
特特特别的说明 CentOS发行版已经不再适合应用于生产环境,客观条件不得不用的话,优选7.9版本,8.5版本次之,最次6.10版本(比如说Oracle 11GR2就建议在6版本上部署)! 引导和开始安装 选择倒计时结…...

好用的流程图工具
分享工作中常用的装逼工具 目前市面上的流程图或者思维导图工具挺多的,但是有的会限制使用数量或者收费,典型的有processon、Xmind,推荐今天Mermaid(官网)。 快速上手 中文教程:Mermaid 初学者用户指南 | Mermaid 中文网。我们选择…...

数据结构:链式栈
stack.h /* * 文件名称:stack.h * 创 建 者:cxy * 创建日期:2024年01月18日 * 描 述: */ #ifndef _STACK_H #define _STACK_H#include <stdio.h> #include <stdlib.h>typedef struct stack{int data…...
openssl3.2 - 官方demo学习 - mac - gmac.c
文章目录 openssl3.2 - 官方demo学习 - mac - gmac.c概述笔记END openssl3.2 - 官方demo学习 - mac - gmac.c 概述 使用GMAC算法, 设置参数(指定加密算法 e.g. AES-128-GCM, 设置iv) 用key执行初始化, 然后对明文生成MAC数据 官方注释给出建议, key, iv最好不要硬编码出现在程…...

HugggingFace 推理 API、推理端点和推理空间相关模型部署和使用以及介绍
HugggingFace 推理 API、推理端点和推理空间相关模型部署和使用以及介绍。 Hugging Face是一家开源模型库公司。 2023年5月10日,Hugging Face宣布C轮1亿美元融资,由Lux Capital领投,红杉资本、Coatue、Betaworks、NBA球星Kevin Durant等跟投…...

python的tabulate包在命令行下输出表格不对齐
用tabulate可以在命令行下输出表格。 from tabulate import tabulate# 定义表头 headers [列1, 列2, 列3]# 每行的内容 rows [] rows.append((张三,数学,英语)) rows.append((李四,信息科技,数学))# 使用 tabulate 函数生成表格 output tabulate(rows, headersheaders, tab…...

LLM之幻觉(二):大语言模型LLM幻觉缓减技术综述
LLM幻觉缓减技术分为两大主流,梯度方法和非梯度方法。梯度方法是指对基本LLM进行微调;而非梯度方法主要是在推理时使用Prompt工程技术。LLM幻觉缓减技术,如下图所示: LLM幻觉缓减技术值得注意的是: 检索增强生成&…...
C# 使用多线程,关闭窗体时,退出所有线程
this.Close(); 只是关闭当前窗口,若不是主窗体的话,是无法退出程序的,另外若有托管线程(非主线程),也无法干净地退出;Application.Exit(); 强制所有消息中止,退出所有的窗体&…...

数据结构实验6:图的应用
目录 一、实验目的 1. 邻接矩阵 2. 邻接矩阵表示图的结构定义 3. 图的初始化 4. 边的添加 5. 边的删除 6. Dijkstra算法 三、实验内容 实验内容 代码 截图 分析 一、实验目的 1.掌握图的邻接矩阵的存储定义; 2.掌握图的最短路径…...

Spring Boot整合JUnit
引言 测试是软件开发过程中不可或缺的一环,而JUnit作为Java生态中最流行的测试框架之一,与Spring Boot的整合为开发者提供了一套强大的测试工具。本文将讨论Spring Boot整合JUnit的技术细节、最佳实践以及测试驱动开发(TDD)的优雅…...
uniapp写小程序实现清除缓存(存储/获取/移除/清空)
在uni-app中,可以使用uni.setStorageSync和uni.getStorageSync来进行数据的存储和获取。而移除缓存数据可以使用uni.removeStorageSync,清空缓存数据可以使用uni.clearStorageSync。 以下是使用示例: 存储数据: uni.setStorage…...

js菜单隐藏显示
1、树状结构对应的表: 2、生成menulist的SQL语句 select {"id":"MenuID","parent":"ParentID","FirstLvMenu":"FirstLvMenu", "text":"MenuName","url":"MenuUrl",&quo…...

学习Spring的第五天(Bean的依赖注入)
Bean的依赖注入有两种方式: 一 . 常规Bean的依赖注入 很简单,不过多赘述了,注意ref: 是构造函数或set方法的参数,一般为对象, value: 是构造函数或set方法的参数,一般为值. 看下图 1.1 下面来演示一下集合数据类型的关于Bean的依赖注入 1.1.1这是List的注入(演示泛型为Strin…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...

ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题
分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...
作为测试我们应该关注redis哪些方面
1、功能测试 数据结构操作:验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化:测试aof和aof持久化机制,确保数据在开启后正确恢复。 事务:检查事务的原子性和回滚机制。 发布订阅:确保消息正确传递。 2、性…...