上一篇:性能测试工具 下一篇:原型优化

循环专题

循环专题

  • 循环是一种常用的流程控制。

    • JAVASCRIPT提供了三种循环。

      • for(;;)

        • 推荐使用for循环,如果循环变量递增或递减,不要单独对循环变量赋值,而应该使用嵌套的++–-运算符。
        • 代码的可读性对于for循环的优化。
        • -=1
        • 从大到小的方式循环(这样缺点是降低代码的可读性)。

          /**效率低**/
          var divs = document.getElementsByTagName("div");
          for(var i = 0; i < divs.length; i++){
              ...
          }
          /**效率高,适用于获取DOM集合,如果纯数组则两种情况区别不到**/
          var divs = document.getElementsByTagName("div");
          for(var i = 0, len = divs.length; i < len; i++){
              ...
          }
          /**在`IE6.0`下,`for(;;)`循环在执行中,第一种情况会每次都计算一下长度,而第二种情况却是在开始的时候计算长度,并把其保存到一个变量中,所以其执行效率要高点,所以在我们使用`for(;;)`循环的时候,特别是需要计算长度的情况,我们应该开始将其保存到一个变量中。**/
      • while()
        • for(;;)while()循环的性能基本持平。
      • for(in)
        • 在这三种循环中for(in)内部实现是构造一个所有元素的列表,包括array继承的属性,然后再开始循环,并且需要查询hasOwnProperty。所以for(in)相对for(;;)循环性能要慢。
  • 选择正确的方法

    • 避免不必要的属性查找。

      • 访问变量数组O(1)操作。
      • 访问对象上的属性是一个O(n)操作。

      对象上的任何属性查找都要比访问变量或数组花费更长时间,因为必须在原型链中对拥有该名称的属性进行一次搜索,即属性查找越多,执行时间越长。所以针对需要多次用到对象属性,应将其存储在局部变量。

    • 优化循环。

      • 减值迭代。
        • 大多数循环使用一个从0开始,增加到某个特定值的迭代器。在很多情况下,从最大值开始,在循环中不断减值的迭代器更加有效。
      • 简化终止条件。
        • 由于每次循环过程都会计算终止条件,故必须保证它尽可能快,即避免属性查找或其它O(n)的操作。
      • 简化循环体。
        • 循环体是执行最多的,故要确保其被最大限度地优化。确保没有某些可以被很容易移出循环的密集计算。
      • 使用后测试循环。
        • 最常用的for和while循环都是前测试循环,而如do-while循环可以避免最初终止条件的计算,因些计算更快。 > for(var i = 0; i 优化1:简化终止条件 > for(var i = 0, len = values.length; i 优化2:使用后测试循环(注意:使用后测试循环需要确保要处理的值至少有一个) > var i values.length - 1; if(i > -1) { do { process(values[i]); }while(--i >= 0); }
    • 展开循环。
      • 当循环的次数确定时,消除循环并使用多次函数调用往往更快。
      • 当循环的次数不确定时,可以使用Duff装置来优化。
        • Duff装置的基本概念是通过计算迭代的次数是否为8的倍数将一个循环展开为一系列语句。 > // Jeff Greenberg for JS implementation of Duff's Device // 假设:values.length > 0 function process(v) { alert(v); } var values = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]; var iterations = Math.ceil(values.length / 8); var startAt = values.length % 8; var i = 0; do { switch(startAt) { case 0 : process(values[i++]); case 7 : process(values[i++]); case 6 : process(values[i++]); case 5 : process(values[i++]); case 4 : process(values[i++]); case 3 : process(values[i++]); case 2 : process(values[i++]); case 1 : process(values[i++]); } startAt = 0; }while(--iterations > 0); > 如上展开循环可以提升大数据集的处理速度。接下来给出更快的Duff装置技术,将do-while循环分成2个单独的循环。(注:这种方法几乎比原始的Duff装置实现快上40%。) > // Speed Up Your Site(New Riders, 2003) function process(v) { alert(v); }
          var values = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]; var iterations = Math.floor(values.length / 8); var leftover = values.length % 8; var i = 0; if(leftover > 0) { do { process(values[i++]); }while(--leftover > 0); }
          do { process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); process(values[i++]); }while(--iterations > 0); > 针对大数据集使用展开循环可以节省很多时间,但对于小数据集,额外的开销则可能得不偿失。
  • 避免在循环中使用try-catch

    • try-catch-finally语句在catch语句被执行的过程中会动态构造变量插入到当前域中,对性能有一定影响。

    • 如果需要异常处理机制,可以将其放在循环外层使用。

      • 循环中使用try-catch

        for ( var i = 0; i < 200; i++) {
          try {} catch (e) {}
        }
      • 循环外使用try-catch

        try {
          for ( var i = 0; i < 200; i++) {}
        } catch (e) {}
  • 避免遍历大量元素:

    • 避免对全局DOM元素进行遍历,如果parent已知可以指定parent在特定范围查询。

      var elements = document.getElementsByTagName( '*' );
      for (i = 0; i < elements.length; i++) {
         if (elements[i].hasAttribute( 'selected' )) {}
      }

      如果已知元素存在于一个较小的范围内,

      var elements = document.getElementById( 'canvas' ).getElementsByTagName ( '*' );
      for (i = 0; i < elements.length; i++) {
         if (elements[i].hasAttribute( 'selected' )) {}
      }
上一篇:性能测试工具 下一篇:原型优化