在前端 js 中,有着多种数组循环的方式:

  1. for 循环;
  2. while 和 do-while 循环;
  3. forEach、map、reduce、filter 循环;
  4. for-of 循环;
  5. for-in 循环;

那么哪种循环的执行速度最快呢,我们今天来看一看。

在测试循环速度之前,我们先来创建一个有 100 万数据的数组:

const len = 100 * 10000;
const arr = [];
for (let i = 0; i < len; i++) {
arr.push(Math.floor(Math.random() * len));
}

测试环境为:

  • 电脑:iMac(10.13.6);
  • 处理器:4.2 GHz Intel Core i7;
  • 浏览器:Chrome(89.0.4389.82)

1. for 循环

for 循环是我们最常用的一种循环方式了,最大的好处就是结构清晰,能够随时 break 停止。

我们先用 10 次的测试来看下:

console.log('test for');
for (let k = 0; k < 10; k++) {
console.time('for');
let sum = 0;
for (let i = 0; i < len; i++) {
sum += arr[i] % 100;
}
console.timeEnd('for');
}

最终得到的结果:

在第 1 次和第 2 次时耗时比较多,从第 3 次开始就一直维持在 1.25ms 左右。

2. while 循环和 do-while 循环

这两个放在一起,也是他们的结构足够像,而且也能够随时 break 停止。

console.log('\ntest while');
for (let k = 0; k < 10; k++) {
console.time('while');
let sum = 0;
let i = 0;
while (i < len) {
sum += arr[i] % 100;
i++;
}
console.timeEnd('while');
}
console.log('\ntest do-while');
for (let k = 0; k < 10; k++) {
console.time('do-while');
let sum = 0;
let i = 0;
do {
sum += arr[i] % 100;
i++;
} while (i < len);
console.timeEnd('do-while');
}

while 循环和 do-while 循环的结果几乎一样,我们只看下 while 循环在浏览器上运行的结果:

跟 for 循环的速度不相上下。

3. forEach、map 和 reduce 循环

接下来来到我们常用的数组三剑客了:forEach, map, reduce 等,这 3 个方法都是在 ES6 标准上新加的语法。

3.1 forEach 的简要介绍

这几种方法是无法停止循环的,无论使用break还是return,都无法停止整个循环。我们可以做一个测试,例如我想当遇到 3 的倍数时,即停止循环

[1, 2, 3, 4, 5].forEach((item) => {
console.log(`before return: ${item}`);
if (item % 3 === 0) {
return;
}
console.log(`after return: ${item}`);
});

运行结果如下:

从运行的结果可以看到,我们的 return 只是没有执行当时循环时后面的语句,但并没有停止整个循环,后面的 4 和 5 依然正常输出。

那循环是否真的像炫迈一样停不下来吗?并不,还有一种方式,可以停止循环。那就是抛出异常

try {
[1, 2, 3, 4, 5].forEach((item) => {
console.log(`before return: ${item}`);
if (item % 3 === 0) {
throw new Error('break forEach');
}
console.log(`after return: ${item}`);
});
} catch (e) {}

在 forEach 中抛出异常后,就可以停止该循环,然后再使用try-catch捕获异常,避免整个服务被挂掉。

虽然可以停止 forEach 的循环,但实现起来麻烦了不少。因此若没有停止整个循环的需求,可以使用 forEach, map 等循环方式;否则还是要使用其他的循环方式。

3.3 forEach 等的测速

好的,接下来我们就要测试这 3 个循环方式的循环速度了。

// forEach 的测试:
console.log('\ntest forEach');
for (let k = 0; k < 10; k++) {
console.time('forEach');
let sum = 0;
arr.forEach((item) => {
sum += item % 100;
});
console.timeEnd('forEach');
}
// map 的测试:
console.log('\ntest map');
for (let k = 0; k < 10; k++) {
console.time('map');
let sum = 0;
arr.map((item) => {
sum += item % 100;
});
console.timeEnd('map');
}
// reduce 的测试:
console.log('\ntest reduce');
for (let k = 0; k < 10; k++) {
console.time('reduce');
let sum = 0;
arr.reduce((_, item) => {
sum += item % 100;
}, 0);
console.timeEnd('reduce');
}

因这 3 个循环的时间差不多,我这里就只截取了 forEach 的测试结果。

执行 10 次循环后,forEach 的执行时间差不多在 10.8ms 左右,比上面的 for 循环和 while 循环高了将近 10 倍的运行时间。

4. for-of

ES6 借鉴 C++、Java、C# 和 Python 语言,引入了 for...of 循环,作为遍历所有数据结构的统一的方法。

4.1 for-of 的简要介绍

一个数据结构只要部署了 Symbol.iterator 属性,就被视为具有 iterator 接口,就可以用 for...of 循环遍历它的成员。也就是说,for...of 循环内部调用的是数据结构的 Symbol.iterator 方法。

for...of 循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如 arguments 对象、DOM NodeList 对象)、后文的 Generator 对象,以及字符串。

for-of 拿到的就是 value 本身,而 for-in 则拿到的是 key,然后通过 key 再获取到当前数据。

const fruits = ['apple', 'banana', 'orange', 'lemon'];

for (const value of fruits) {
console.log(value); // 'apple', 'banana', 'orange', 'lemon'
}

4.2 for-of 的循环测速

测试 for-of 循环速度的代码:

console.log('\ntest for-of');
for (let k = 0; k < 10; k++) {
console.time('for-of');
let sum = 0;
for (const value of arr) {
sum += value % 100;
}
console.timeEnd('for-of');
}

测试结果:

在多次重复同一个循环时,前 2 次的 for-of 循环时间会比较长,得在 15ms 以上。但后续的执行,循环速度就下降到 1.5ms 左右了,与 for 循环的时间差不多。

5. for-in 循环

for-in 通常用于 object 类型的循环,但也可以用来循环数组,毕竟所有数据类型的祖先都是 object 类型。

console.log('\ntest for-in');
for (let k = 0; k < 10; k++) {
console.time('for-in');
let sum = 0;
for (let key in arr) {
sum += arr[key] % 100;
}
console.timeEnd('for-in');
}

测试结果:

for-in 循环的测速数据很惊人,简直是独一档的存在了,最好的时候也至少需要 136ms 的时间。可见 for-in 的循环效率真的很低。

数组类型的数据还是不要使用采用 for-in 循环了;Object 类型的可以通过Object.values()先获取到所有的 value 数据,然后再使用 forEach 循环:

const obj = {};
for (let i = 0; i < len; i++) {
obj[i] = Math.floor(Math.random() * len);
}
for (let k = 0; k < 10; k++) {
console.time('forEach-values');
let sum = 0;
Object.values(obj).forEach((item) => {
sum += item % 100;
});
console.timeEnd('forEach-values');
}

即使多了一步操作,循环时间也大概在 14ms 左右,要比 for-in 快很多。

6. 总结

我们把所有的循环数据放到一起对比一下,我们这里将每个循环的测试次数调整为 100 次,横轴是循环的次数,数轴是循环的时间:

  • for 循环、while 循环和 d-while 循环的时间最少;
  • for-of 循环的时间稍长;
  • forEach 循环、map 循环和 reduce 循环 3 者的数据差不多,但比 for-of 循环的时长更长一点;
  • for-in 循环所需要的时间最多;

每种循环的时长不一样,我们在选择循环方式时,除了考虑时间外,也要考虑到语义化和使用的场景。

如果您喜欢我的文章,也欢迎关注我的公众号:

js 中你不知道的各种循环测速的更多相关文章

  1. js事件、Js中的for循环和事件的关系、this

    一.js事件  1.事件 用户在网页中所触发的行为 鼠标滑动种类很多,键盘.表单特列: 点击:onclick 鼠标进入:onmouseenter 鼠标离开:onmouseleave 鼠标悬浮:onmo ...

  2. php中的for循环和js中的for循环

    php中的for循环 循环100个0 for ($i=0;$i<=100;$i++){ $pnums.='0'.","; } js中的for循环,循环31个相同的数.循环日期 ...

  3. 【nodejs原理&源码赏析(7)】【译】Node.js中的事件循环,定时器和process.nextTick

    [摘要] 官网博文翻译,nodejs中的定时器 示例代码托管在:http://www.github.com/dashnowords/blogs 原文地址:https://nodejs.org/en/d ...

  4. 【nodejs原理&源码赏析(7)】【译】Node.js中的事件循环,定时器和process.nextTick

    目录 Event Loop 是什么? Event Loop 基本解释 事件循环阶段概览 事件循环细节 timers pending callbacks poll阶段 check close callb ...

  5. js中跳出forEach循环

    缘由:近期在项目中使用lodash.js中的_.foreach方法处理数据,需要在满足条件时结束循环并不执行后面的js代码. 因为foreach中默认没有break方法.在尝试中使用了return f ...

  6. js中数组的循环与遍历forEach,map

    对于前端的循环遍历我们知道有 针对js数组的forEach().map().filter().reduce()方法 针对js对象的for/in语句(for/in也能遍历数组,但不推荐) 针对jq数组/ ...

  7. Js中的For循环详解

    大家好,我是逆战班的一员,今天给大家讲解一下Js循环中的For循环. For循环是JS循环中一个非常重要的部分. 我们先讲一下for循环的作用: For循环用在需要重复执行的某些代码,比如从1打印到1 ...

  8. js 中常用的循环

    参考文章: 1.js几种数组遍历方式以及性能分析对比 2.javaScript遍历对象.数组总结 首先是数组中可以使用的 1.for 循环 for (let i = 0; i < xxx.len ...

  9. JS中for...in循环陷阱及遍历数组的方式对比

    JavaScript中有很多遍历数组的方式,比较常见的是for(var i=0;i<arr.length;i++){},以及for...in...循环等,这些遍历都有各自的优缺点,下面来看看各种 ...

  10. node.js 中模块的循环调用问题详解

    首先,我们看一下图示代码,每一个注释其实代表一个 js 文件.所以下面其实是三个 js 文件 .第一个是我们要运行的 main 文件,后面两个是 a, b 文件.   从上面可以看书 a ,b 两个模 ...

随机推荐

  1. MogDB-opengauss中的聚集与分组操作

    MogDB/opengauss 中的聚集与分组操作 COUNT:对结果集中的元组数量进行计数,如果是 COUNT(*),那么会统计所有元组(包括 NULL 值)的数量,如果是 COUNT(colnam ...

  2. HarmonyOS开发案例分享:万能卡片也能用来玩游戏

    一.前言 作为一名开发爱好者,从大了讲,我学习并进行HarmonyOS相关开发是为了能为鸿蒙生态建设尽一份绵薄之力,从小了讲,就是为了自己的兴趣.而万能卡片是一个让我非常感兴趣的东西. 很多时候我跟别 ...

  3. 进阶 stack smashing--canary 报错利用 && environ泄露栈地址

    进阶 stack smashing--canary 报错利用 && environ泄露栈地址 这部分是对进阶stack smashing的使用,以及对 environ的认识,我们可以看 ...

  4. Centos7配置vnc

    VNC服务:VNC(Virtual Network Console)是虚拟网络控制台的缩写.它 是一款优秀的远程控制工具软件,由著名的 AT&T 的欧洲研究实验室开发的.VNC 是在基于 UN ...

  5. “AI虚拟数字人+线下大屏互动”升级智能人机交互服务!

    如今AIGC 强势爆发. ChatGPT 语言大模型横空出世,使得数字人的"大脑"水平得到了极大提升,AI技术赋能下的虚拟数字人拥有了更加精准的语言表达.思考逻辑.帮助各个行业实现 ...

  6. TypeScript 中接口的理解?应用场景?

    一.是什么 「接口」是一系列抽象方法的声明,是一些方法特征的集合,这些方法都应该是抽象的,需要由具体的「类」去实现,然后第三方就可以通过这组抽象方法调用,让具体的类执行具体的方法 简单来讲,一个接口所 ...

  7. Causal Inference理论学习篇-Tree Based-Causal Forest

    广义随机森林 了解causal forest之前,需要先了解其forest实现的载体:GENERALIZED RANDOM FORESTS[6](GRF) 其是随机森林的一种推广, 经典的随机森林只能 ...

  8. 力扣539(java)-最小时间差(中等)

    题目: 给定一个 24 小时制(小时:分钟 "HH:MM")的时间列表,找出列表中任意两个时间的最小时间差并以分钟数表示. 示例 1: 输入:timePoints = [" ...

  9. Kubernetes 稳定性保障手册 -- 极简版

    简介: Kubernetes 在生产环境中的采用率越来越高,复杂度越来越高,由此带来的稳定性保障的挑战越来越大. Kubernetes 在生产环境中的采用率越来越高,复杂度越来越高,由此带来的稳定性保 ...

  10. SLS控制台内嵌操作指南

    简介: SLS控制台内嵌操作指南 一.机制 详见:https://help.aliyun.com/document_detail/74971.html 二.操作 2.1 子账号操作(主账号身份操作) ...