关于前端面试中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时中断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如何优化呢?我们可以这样优化

// 全部数据
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?什么是点击劫持?如何防御

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

步进电机基本原理详解

步进电机基本原理 步进电机是一种将电脉冲信号转换成相应角位移或线位移的电动机。 区别于以电压或电流作为控制信号&#xff0c;被控制量是转速的电动机。 每输入一个脉冲信号&#xff0c;转子就转动一个角度或前进一步&#xff0c;其输出的角位移或线位移与输入的脉冲数成正…

electron+vue项目使用serialport报错Cannot read property ‘indexOf‘ of undefined解决办法

描述 使用ElectronVue项目时引入serialport串口后启动时报下面错误 Cannot read property indexOf of undefined解决方法 打开vue.config.js找到pluginOptions -> electronBuilder -> externals添加serialport module.exports {pluginOptions: {electronBuilder: {e…

SAP中采购文档价格条件可以删除吗?

首先要声名&#xff0c;基于采购价格条件的严谨性和历史追朔需求&#xff0c;删除属于危险操作。不建议普通用户去执行操作。如果有兴趣&#xff0c;在测试系统中自行测试一下即可。正式系统中&#xff0c;还请慎重处理。 笔者公司日常不会去删除采购价格&#xff0c;日常处理…

网络共享服务

存储类型&#xff1a;直连式&#xff08;DAS&#xff09;:距离最近&#xff0c;存储设备且直接连接到服务器上 存储区域网络&#xff08;SAN&#xff09;&#xff1a;适用于大型应用或数据库系统&#xff0c;可以使用文件的空间&#xff0c; 以及管理空间…

【发票识别】支持pdf、ofd、图片格式(orc、信息提取)的发票

背景 为了能够满足识别各种发票的功能&#xff0c;特地开发了当前发票识别的功能&#xff0c;当前的功能支持pdf、ofd、图片格式的发票识别&#xff0c;使用到的技术包括文本提取匹配、ocr识别和信息提取等相关的技术&#xff0c;用到机器学习和深度学习的相关技术。 体验 体…

【蓝桥杯日记】第二篇——递归问题的处理

目录 前言 递归 递归解决的问题 递归的三要素 递归的练习&#xff08;由浅入深&#xff09; 1.循环改为递归 2.斐波那契 3.汉诺塔问题 总结 前言 大家好呀&#xff01;我是大雄&#xff01;一个菜鸡&#xff01;接下来的几个月和大家分享一下自己在备战蓝桥中遇到的…

Vue学习计划-Vue3--核心语法(十)Proxy响应式原理

Proxy响应式原理 1.Vue2的响应式 实现原理&#xff1a; 对象类型&#xff1a;通过Object.defineProperty()对属性的读取、修改进行拦截&#xff08;数据劫持&#xff09;数组类型&#xff1a;通过重写更新数组的一系列方法来实现拦截&#xff0c;&#xff08;对数组的变更方法…

【Linux】各目录说明

【常见目录说明】 目录 /bin 存放二进制可执行文件(ls,cat,mkdir等)&#xff0c;常用命令一般都在这里。 /etc 存放系统管理和配置文件 /home 存放所有用户文件的根目录&#xff0c;是用户主目录的基点&#xff0c;比如用户user的主目录就是/home/user&#xff0c;可以…

浅谈智慧路灯安全智能供电方案设计

摘要: 智慧路灯&#xff0c;作为智慧城市、新基建、城市更新的主要组成部分&#xff0c;近些年在各大城市已得到很好的落地和 应用&#xff0c;但其与传统路灯相比集成大量异元异构电子设备&#xff0c;这些设备的供电电压、接口形式、权属单位各不相同&#xff0c; 如何设计一…

houdini rnn

1.3.RNN模型_哔哩哔哩_bilibili 此公式来自于吴恩达P1.3视频 按公式推测rnn内部结构,如有错误&#xff0c;敬请指正

mysql-锁

文章目录 概念隔离级别未提交读&#xff08;READ UNCOMMITTED&#xff09;提交读&#xff08;READ COMMITTED&#xff09;可重复读&#xff08;REPEATABLE READ&#xff09;可串行化&#xff08;SERIALIZABLE&#xff09; 锁分类按性能乐观锁&#xff08;用版本对比来实现&…

python入门,数据容器的通用操作(len,max,min,sorted)

1.len统计容器内元素个数 2.max统计元素最大元素 3.min统计元素最小元素 4.容器的转化功能 list&#xff08;容器&#xff09;将给定容器转化为列表 字符串转列表将字符串内的每一个元素都取了出来作为列表的每一个元素 字典则只会取出它的key&#xff0c;value会消失 str&…

在线SM2密钥生成工具

在线SM2密钥生成工具 - BTool在线工具软件&#xff0c;为开发者提供方便。本工具为你提供便捷的SM2密钥生成功能。SM2是中国国家密码管理局颁布的中国商用公钥密码标准算法(一种非对称加密算法)&#xff0c;SM2采用的是ECC 256位的一种椭圆曲线的加密算法,其密钥长度256bit&…

go中如何进行单元测试案例

一. 基础介绍 1. 创建测试文件 测试文件通常与要测试的代码文件位于同一个包中。测试文件的名称应该以 _test.go 结尾。例如&#xff0c;如果你要测试的文件是 math.go&#xff0c;那么测试文件可以命名为 math_test.go。 2. 编写测试函数 测试函数必须导入 testing 包。每…

HIve项目入门 环境部署遇到的问题及解决方案

环境布置的步骤建议是jdk, hadoop hive这几个分别去下载&#xff0c;参考以下几个安装教程&#xff1a; 【主要参考&#xff1a;傻瓜式教程】Windows下安装Hive MySQL版【附安装Hadoop教程】全网最详细的图文教程 【有一些补充的内容】&#xff1a;Windows下安装Hive 遇到的几个…

2024.1.16每日一题

LeetCode 2719.统计整数数目 2719. 统计整数数目 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给你两个数字字符串 num1 和 num2 &#xff0c;以及两个整数 max_sum 和 min_sum 。如果一个整数 x 满足以下条件&#xff0c;我们称它是一个好整数&#xff1a; num1 &l…

QT 原生布局和QML的区别

一、QML 与 Qt Quick的区别 1.1 从概念上区分 为了更精确地对两者进行说明&#xff0c;先看助手对 QML 的描述&#xff1a; QML is a user interface specification and programming language. QML 是一种用户界面规范和标记语言&#xff0c;允许开发人员和设计师创建高性能、流…

招生官怒批ChatGPT文书质量“缺少灵魂”

ChatGPT无疑是最近两年留学届的热门话题&#xff0c;也成为了不少留学生再也离不开的万能工具&#xff0c;从总结文献、润色论文、给教授写email似乎无所不能。甚至还有不少同学在考虑直接提交ChatGPT生成的文书。 那么ChatGPT生成的文书质量高吗&#xff1f;各大高校对于学生…

【NI国产替代】PXI-6254,32 AI(16位,1 MS/s),48 DIO,PXI多功能I/O模块

32 AI&#xff08;16位&#xff0c;1 MS/s&#xff09;&#xff0c;48 DIO&#xff0c;PXI多功能I/O模块 PXI-6254提供模拟输入、关联数字I/O、两个32位计数器/定时器以及模拟和数字触发。该设备为从实验室自动化、研究、设计验证/测试到制造测试等各种应用提供了低成本的可靠D…

基于ssm的疫苗预约系统论文

摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装疫苗预约系统软件来发挥其高效地信息处理的作用&#xff0c…