详解JS中你不知道的各种循环测速

前言

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

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

测试环境为:

1.电脑:iMac(10.13.6);

2.处理器:4.2 GHz Intel Core i7;

3.浏览器: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.2 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 次,横轴是循环的次数,数轴是循环的时间:

1.for 循环、while 循环和 d-while 循环的时间最少;

2.for-of 循环的时间稍长;

3.forEach 循环、map 循环和 reduce 循环 3 者的数据差不多,但比 for-of 循环的时长更长一点;

4.for-in 循环所需要的时间最多;

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

以上就是详解JS中你不知道的各种循环测速的详细内容,更多关于JS中各种循环测速的资料请关注我们其它相关文章!

(0)

相关推荐

  • 利用JS测试目标网站的打开响应速度

    闲来无事,用JS写了一个简单的测试目录网站打开速度的小东西,注意这个只是本机打开目录网站的速度,不代表其它用户的打开也是这个速度,实际上也可用于测试本地网络速度 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http:

  • js实现从右往左匀速显示图片(无缝轮播)

    本文实例为大家分享了js实现从右往左匀速显示图片的具体代码,供大家参考,具体内容如下 前言: 匀速显示图片,一般用于重复显示公司活动系列图片 背景图片: <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8" /> <style type="text/css"> *{ margin: 0; padding: 0; } .

  • JS eval代码快速解密实例解析

    有一段js代码内容如下: eval(function(E,I,A,D,J,K,L,H){function C(A)后面内容省略... 解密可以采用如下方法: 方法一: 打开谷歌浏览器,按F12,在Console窗口中把eval代码复制粘贴进去,回车运行,即可就到源码. 方法二: 新建一个html文件,把上面eval替换成document.write输出即可. 备注,前后加xmp标签的作用是完整的输出html标签,并且不做任何转义. <html> <head> <title&g

  • JavaScript运动框架 解决速度正负取整问题(一)

    这里说的运动是指缓冲运动,缓冲运动会使物体逐渐'着陆',而不是'硬着陆',到达目标位置的过程中速度越来越慢,看起来很舒服. 缓冲的特点: 速度随着距离的缩短而降低 速度 = (目标值 - 当前值) / 缩放系数: 速度一定要是整数 比如,一个div从最左边运动到left等于400的位置停下,可以如下实现: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>运

  • Javascript中八种遍历方法的执行速度深度对比

    前言 遍历数组或对象是一名程序员的基本素养之一. 然而遍历却不是一件简单的事, 优秀的程序员知道怎么去选择合适的遍历方法, 优化遍历效率. 本篇将带你走进JavaScript遍历的世界, 享受分析JS循环的快感. 本篇所有代码都可以直接运行, 希望您通读本篇后, 不止是浏览, 最好是亲手去实践下. 概述 js有如下两种数据需要经常遍历 数组(Array) 对象(Object) 同时又提供了如下8种方法方便我们遍历元素 for while(或do~while) forEach for in $.e

  • Node.js 如何利用异步提升任务处理速度

    今天在做一个小任务,需要调用阿里云的图像识别接口,对 62662 张照片进行场景识别,并将结果写到本地的 csv 文件中. 因为任务很简单,没想很多就开始码.自从有了 async/await 之后,已经很久不写 callback 了,所以上手就写成这样: 本文所有代码均有简化,只保留关键过程 async fetchSceneTags(imagePath) { try { const result = await callAliyunAPI(imagePath); return result.er

  • 如何提高javascript加载速度

    方法如下: 1.将所有<script>标签放在尽可能接近<body>标签底部的位置,以保证页面在脚本运行之前完成解析尽量减少对整个页面下载的影响 2.限制页面的<script>总数也可以改善性能.每当页面解析碰到一个<script>标签时, 紧接着有一段时间用于代码执行.最小化这些延迟时间可以改善页面的整体性能. 3.减少引用外部脚本文件的数量.每个 HTTP 请求都会产生额外的性能负担,下载一个 100KB 的文件比下载四个 25KB 的文件要快.总之,减

  • JS快速实现简单计算器

    本文实例为大家分享了JS实现简单计算器的具体代码,供大家参考,具体内容如下 直接上图 HTML部分 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>我的第一个计算器</title> <link rel="stylesheet" href="计算器.css" rel="external no

  • 基于JS实现快速读取TXT文件

    1 前言 最近有个需求,需要使用JS快速读取外部大数据文件(60w条记录的表).笔者尝试过使用JS读取Excel文件,但是跑了十几分钟仍未出结果,后来笔者尝试将原数据保存为TXT文件,再从TXT文件中读取数据,只需几秒钟即可读取完毕.在此分享一下,也留着以后备用. 2 案例 为方便快速理解,笔者挑选了一个数据量小.业务逻辑简单的案例:从TXT文件中读取数据,并按照原列表格式显示. 工作空间 待读取的TXT文件数据 read.html <!DOCTYPE html> <html> &

  • 详解JS中你不知道的各种循环测速

    前言 在测试循环速度之前,我们先来创建一个有 100 万数据的数组: const len = 100 * 10000; const arr = []; for (let i = 0; i < len; i++) { arr.push(Math.floor(Math.random() * len)); } 测试环境为: 1.电脑:iMac(10.13.6): 2.处理器:4.2 GHz Intel Core i7: 3.浏览器:Chrome(89.0.4389.82) 1. for 循环 for

  • 一文详解JS中的事件循环机制

    目录 前言 1.JavaScript是单线程的 2.同步和异步 3.事件循环 前言 我们知道JavaScript 是单线程的编程语言,只能同一时间内做一件事,按顺序来处理事件,但是在遇到异步事件的时候,js线程并没有阻塞,还会继续执行,这又是为什么呢?本文来总结一下js 的事件循环机制. 1.JavaScript是单线程的 JavaScript 是一种单线程的编程语言,只有一个调用栈,决定了它在同一时间只能做一件事.在代码执行的时候,通过将不同函数的执行上下文压入执行栈中来保证代码的有序执行.在

  • 实例详解JS中的事件循环机制

    目录 一.前言 二.宏.微任务 三.Tick 执行顺序 四.案例详解 1.掺杂setTimeout 2.掺杂微任务,此处主要是Promise.then 3.掺杂async/await 一.前言 之前我们把react相关钩子函数大致介绍了一遍,这一系列完结之后我莫名感到空虚,不知道接下来应该更新有关哪方面的文章.最近想了想,打算先回归一遍JS基础,把一些比较重要的基础知识点回顾一下,然后继续撸框架(可能是源码.也可能补全下全家桶).不积跬步无以至千里,万丈高楼咱们先从JS的事件循环机制开始吧,废话

  • 详解JS中的reduce fold unfold用法

    fold(reduce) 说说reduce吧, 很喜欢这个函数,节省了不少代码量,而且有一些声明式的雏形了,一些常见的工具函数,flatten,deepCopy,mergeDeep等用reduce实现的很优雅简洁.reduce也称为fold,本质上就是一个折叠数组的过程,把数组中的多个值经过运算变成一个值,每次运算都会有一个函数处理,这个函数就是reduce的核心元素,称之为reducer,reducer函数是个2元函数,返回1个单值,常见的add函数就是reducer const addRed

  • 详解JS中continue关键字和break关键字的区别

    目录 1.框架 2.简单介绍 3.代码演示 4.演示break 1.框架 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <script> </script> </body> </html> 2.简单介绍 1.在javascr

  • 详解javaweb中jstl如何循环List中的Map数据

    详解javaweb中jstl如何循环List中的Map数据 第一种方式: 1:后台代码(测试) List<Map<String, Object>> list = new ArrayList<Map<String,Object>>(); Map<String, Object> map = null; for (int i = 0; i < 4; i++) { map = new HashMap<String, Object>();

  • 详解js中构造流程图的核心技术JsPlumb(2)

    前言:上篇详解js中构造流程图的核心技术JsPlumb介绍了下JsPlumb在浏览器里面画流程图的效果展示,以及简单的JsPlumb代码示例.这篇还是接着来看看各个效果的代码说明. 一.设置连线的样式和颜色效果代码示例 大概的效果如图: 这些效果看着很简单,那么,我们如何用代码去实现它呢.上章我们说过,JsPlumb的连线样式是由点的某些属性决定的,既然如此,我们就通过设置点的样式来动态改变连线的样式即可.来看代码: 首先来看看连线类型的那个select <div id="btn_line

  • 详解js中的几种常用设计模式

    工厂模式 function createPerson(name, age){ var o = new Object(); // 创建一个对象 o.name = name; o.age = age; o.sayName = function(){ console.log(this.name) } return o; // 返回这个对象 } var person1 = createPerson('ccc', 18) var person2 = createPerson('www', 18) 工厂函数

  • 详解js中的原型,原型对象,原型链

    理解原型 我们创建的每一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法.看如下例子: function Person(){ } Person.prototype.name = 'ccc' Person.prototype.age = 18 Person.prototype.sayName = function (){ console.log(this.name); } var person1 = ne

  • 详解JS中的compose函数和pipe函数用法

    compose函数 compose函数可以将需要嵌套执行的函数平铺,嵌套执行就是一个函数的返回值将作为另一个函数的参数.我们考虑一个简单的需求:这个需求很简单,直接一个计算函数就行: const calculate = x => (x + 10) * 10; let res = calculate(10); console.log(res); // 200 但是根据我们之前讲的函数式编程,我们可以将复杂的几个步骤拆成几个简单的可复用的简单步骤,于是我们拆出了一个加法函数和一个乘法函数: cons

随机推荐