zoukankan      html  css  js  c++  java
  • js 中你不知道的各种循环测速

    在前端 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');
    }
    

    最终得到的结果:

    for循环的测试结果

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

    是真的

    2. while 循环和 do-while 循环

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

    console.log('
    test 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('
    test 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 循环在浏览器上运行的结果:

    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}`);
    });
    

    运行结果如下:

    想要停止forEach循环

    从运行的结果可以看到,我们的 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('
    test 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('
    test 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('
    test 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 的测试结果。

    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('
    test 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');
    }
    

    测试结果:

    for-of循环的测速结果

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

    5. for-in 循环

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

    console.log('
    test 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循环的测速结果

    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 循环所需要的时间最多;

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

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

    蚊子的公众号

  • 相关阅读:
    设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。写出程序。
    数组模拟栈(C语言)
    JDBC数据源连接池的配置和使用实例
    servlet 具体实现
    MySQL查询数据表中数据记录(包括多表查询)
    java开发中的23中设计模式
    eclipse使用和优化配置
    将博客搬至CSDN
    itext poi 学习之旅 (3)读取数据库信息并由excel展现出来
    itext poi 学习之旅 (2)创建excel
  • 原文地址:https://www.cnblogs.com/xumengxuan/p/14576561.html
Copyright © 2011-2022 走看看