在前端 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. Prometheus之grafana(No data to show)

    一.问题现象 1.grafana添加数据源后获取不到监控数据(No data to show) 2.prometheus以下报错 二.问题原因 服务器与浏览器时间不同步的原因,服务器端配置NTP服务和 ...

  2. 高并发场景QPS等专业指标揭秘大全与调优实战

    高并发场景QPS等专业指标揭秘大全与调优实战 最近经常有小伙伴问及高并发场景下QPS的一些问题,特意结合项目经验和网上技术贴做了一些整理和归纳,供大家参考交流. 一.一直再说高并发,多少QPS才算高并 ...

  3. Matplotlib Installing an official release from resources 源码安装Matplotlib官方版本

    Installation Installing an official release Matplotlib releases are available as wheel packages for ...

  4. aop 阶段性概况

    前言 对aop进行一个阶段性的总结. 正文 首先什么是aop呢? 那么首先看aop的解决什么样的问题. public class Program { public static void Main(s ...

  5. numpy函数向量化,np.vectorize

    import numpy as np import time def myfunc(a, b): if a>b: return a-b else: return a+b vfunc = np.v ...

  6. 使用纯c#在本地部署多模态模型,让本地模型也可以理解图像

    之前曾经分享过纯c#运行开源本地大模型Mixtral-8x7B 当时使用的是llamasharp这个库和Mixtral的模型在本地部署和推理,前段时间我看到llamasharp更新到了0.11.1版本 ...

  7. mysql入门操作(部分操作,不为完全格式)

    查询数据库在电脑中绝对路径: show variables like '%datadir%'; 设置字符集 set names gbk; 导入数据库 source 绝对路径 eg: source D: ...

  8. win11设置笔记本合盖不睡眠

    win11设置笔记本合盖不睡眠 直接搜索控制面板,类型选择大图标,找到电源选项 点击进入电源选项,然后点击选择电源按钮的功能 然后就可以看到一个关闭盖子时,设置成不采取任何操作 然后就可以了

  9. 单元测试必备:Asp.Net Core代码覆盖率实战,打造可靠应用 !

    引言 在前几章我们深度讲解了单元测试和集成测试的基础知识,这一章我们来讲解一下代码覆盖率,代码覆盖率是单元测试运行的度量值,覆盖率通常以百分比表示,用于衡量代码被测试覆盖的程度,帮助开发人员评估测试用 ...

  10. 性能提升一倍!云原生网关支持 TLS 硬件加速

    简介:业界在优化 HTTPS 的性能上也做了诸多探索,传统的软件优化方案有 Session 复用.OCSP Stapling.False Start.dynamic record size.TLS1. ...