zoukankan      html  css  js  c++  java
  • 高性能JavaScript循环语句和条件语句

    这篇文章主要为大家介绍了高性能JavaScript循环语句和条件语句,感兴趣的小伙伴们可以参考一下

     

    一、循环语句
    众所周知,常用的循环语句有for、while、do-while以及for-in,forEach。除了for-in和forEach性能略低外,平时我们对前三者的选择更多的是基于需求而非性能考虑,今天我们就对它们各自的性能做个测试,告诉我们最极端的情况下还能做哪些优化。

    首先我们来谈谈为何for-in和forEach会比其他的慢。for-in一般是用在对象属性名的遍历上的,由于每次迭代操作会同时搜索实例本身的属性以及原型链上的属性,所以效率肯定低下;而forEach是基于函数的迭代(需要特别注意的是所有版本的ie都不支持,如果需要可以用JQuery等库),对每个数组项调用外部方法所带来的开销是速度慢的主要原因。

    接着我们看看每次迭代中for、while以及do-while都做了什么。

    var length = items.length;
    for(var i = 0; i < length; i++)
     process(items[i]);
    
    var j = 0;
    while(j < length) 
     process(items[j++]);
    
    var k = 0;
    do {
     process(items[k++]);
    } while(k < length);
    
    

    上面的每个循环中,每次运行循环体时都会产生这样的操作:

    • 一次控制条件中的数值大小比较(i < length)
    • 一次控制条件结果是否为true的比较(i < length === true)
    • 一次自增操作(i++)
    • 一次数组查找(items[i])
    • 一次函数调用process(items[i])

    我们可以通过颠倒数组的顺序来提高循环性能:

    for(var i = items.length; i--; )
     process(items[i]);
    
    var j = items.length;
    while(j--) 
     process(items[j]);
    
    var k = items.length - 1;
    do {
     process(items[k]);
    } while(k--);
    
    

    本例中使用了倒序循环,并把减法操作整合在循环条件中。现在每个控制条件只是简单地与0比较。控制条件与true值比较,任何非零数会自动转换为true,而零值等同于false。实际上,控制条件从两个比较(迭代数少于总数吗?它是true吗?)减少到一次比较(它是true吗?)。每次迭代从两次比较减少到一次,进一步提高了循环速度。

    性能测试:

    那么事实真的如此吗?真金不怕浏览器验。测试代码很简单,针对不同的8种情况封装了8个函数(不加定时器firefox下无法打印profiles信息,原因不明):

    // init array
    var a = [];
    var length = 10;
    for(var i = 0; i < length; i++)
     a[i] = 1;
    
    function for_in() {
     var sum = 0;
     for(var i in a) 
      sum += a[i];
    }
    
    function for_each() {
     var sum = 0;
     a.forEach(function(value, index, array) {
      sum += value;
     });
    }
    
    function for_normal() {
     var sum = 0;
     for(var i = 0; i < length; i++)
      sum += a[i];
    }
    
    function for_reverse() {
     var sum = 0;
     for(var i = length; i--; )
      sum += a[i];
    }
    
    function while_normal() {
     var sum = 0;
     var i = 0;
     while(i < length) 
      sum += a[i++];
    }
    
    function while_reverse() {
     var sum = 0;
     var i = length;
     while(i--) 
      sum += a[i];
    }
    
    function do_while_normal() {
     var sum = 0;
     var i = 0;
     do {
      sum += a[i++];
     } while(i < length);
    }
    
    function do_while_reverse() {
     var sum = 0;
     var i = length - 1;
     do {
      sum += a[i];
     } while(i--);
    }
    
    setTimeout(function() {
     console.profile();
     for_in();
     for_each();
     for_normal();  
     for_reverse();
     while_normal();
     while_reverse();
     do_while_normal();
     do_while_reverse();
     console.profileEnd();
    }, 1000);
    
    

    当数组长度为100时,我们发现firefox下的结果确实和预料的相似:for-each和for-in效率低下,倒序比正序效率略微提升。(chrome下的profiles由于时间太短不显示)

    当数据量达到100w时,firefox和chrome下的结果都如人所愿,但是也略微有所不同。ff下的for-in表现地比for-each好,而chrome下for-in表现糟糕,直接提出了警告。而倒序迭代虽然性能略微有所提升,但是提升的不是很多,且降低了代码阅读性。

    小结:

    • 倒序迭代确实能略微提升代码性能,但是牺牲了代码可读性,除非追求极端性能优化情况下不然没必要用
    • 遍历数组能用普通的循环就不要用for-in和for-each

    二、条件语句
    常见的条件语句有if-else和switch-case,那么什么时候用if-else,什么时候用switch-case语句呢?

    我们先来看个简单的if-else语句的代码:

    if (value == 0){
      return result0;
    } else if (value == 1){
      return result1;
    } else if (value == 2){
      return result2;
    } else if (value == 3){
      return result3;
    } else if (value == 4){
      return result4;
    } else if (value == 5){
      return result5;
    } else if (value == 6){
      return result6;
    } else if (value == 7){
      return result7;
    } else if (value == 8){
      return result8;
    } else if (value == 9){
      return result9;
    } else {
      return result10;
    }
    

    最坏的情况下(value=10)我们可能要做10次判断才能返回正确的结果,那么我们怎么优化这段代码呢?一个显而易见的优化策略是将最可能的取值提前判断,比如value最可能等于5或者10,那么将这两条判断提前。但是通常情况下我们并不知道(最可能的选择),这时我们可以采取二叉树查找策略进行性能优化。

    if (value < 6){
      if (value < 3){
        if (value == 0){
          return result0;
        } else if (value == 1){
          return result1;
        } else {
          return result2;
        }
      } else {
        if (value == 3){
          return result3;
        } else if (value == 4){
          return result4;
        } else {
          return result5;
        }
      }
    } else {
      if (value < 8){
        if (value == 6){
          return result6;
        } else {
          return result7;
        }
      } else {
        if (value == 8){
          return result8;
        } else if (value == 9){
          return result9;
        } else {
          return result10;
        }
      }
    }
    

    这样优化后我们最多进行4次判断即可,大大提高了代码的性能。这样的优化思想有点类似二分查找,和二分查找相似的是,只有value值是连续的数字时才能进行这样的优化。但是代码这样写的话不利于维护,如果要增加一个条件,或者多个条件,就要重写很多代码,这时switch-case语句就有了用武之地。

    将以上代码用switch-case语句重写:

    switch(value){
      case 0:
        return result0;
      case 1:
        return result1;
      case 2:
        return result2;
      case 3:
        return result3;
      case 4:
        return result4;
      case 5:
        return result5;
      case 6:
        return result6;
      case 7:
        return result7;
      case 8:
        return result8;
      case 9:
        return result9;
      default:
        return result10;
    }
    

    swtich-case语句让代码显得可读性更强,而且swtich-case语句还有一个好处是如果多个value值返回同一个结果,就不用重写return那部分的代码。一般来说,当case数达到一定数量时,swtich-case语句的效率是比if-else高的,因为switch-case采用了branch table(分支表)索引来进行优化,当然各浏览器的优化程度也不一样。

    除了if-else和swtich-case外,我们还可以采用查找表。

    var results = [result0, result1, result2, result3, result4, result5, result6, result7, result8, result9, result10];
    
    //return the correct result
    return results[value];
    

    当数据量很大的时候,查找表的效率通常要比if-else语句和swtich-case语句高,查找表能用数字和字符串作为索引,而如果是字符串的情况下,最好用对象来代替数组。当然查找表的使用是有局限性的,每个case对应的结果只能是一个取值而不能是一系列的操作。

    小结:

    • 当只有两个case或者case的value取值是一段连续的数字的时候,我们可以选择if-else语句
    • 当有3~10个case数并且case的value取值非线性的时候,我们可以选择switch-case语句
    • 当case数达到10个以上并且每次的结果只是一个取值而不是额外的JavaScript语句的时候,我们可以选择查找表

    以上就是本文的全部内容,希望对大家的学习有所帮助。

  • 相关阅读:
    java 删除文件目录
    java导出echart图到excel 多张图片导出
    java导出sql文件
    split方法使用
    jqgrid列表添加单选框
    会消失的链接
    运用javascript做出链接类特效
    创建守护进程的步骤
    ext2文件系统
    C++枚举类型enum
  • 原文地址:https://www.cnblogs.com/johnhery/p/9889064.html
Copyright © 2011-2022 走看看