javascript循环性能大比拼

在设计选择器,我就做了这方面的比较,只不过这次幸运遇到另一个测试程序,它覆盖面更广,测试类型更全面。最后发现自己在选择器中用了一个很慢的循环……囧。由于IE性能比较低下,原程序根本无法运行,因此我把它移到我的博客上来,汉化一下,并减少大量的测试个数(原程序测试个数为1000,我把IE的测试个数减少为300)。但即使这样,它还是弹出警告框问你是否中止循环,你继续按否,就能看到结果了。

 

 
<!doctype html>
<html dir="ltr" lang="zh-CN">
  <head>
    <meta charset="utf-8"/>
    <title>各种循环比较 by fm</title>
    <style type="text/css">
      body{font-family:helvetica,arial,sans-serif;font-size:12px;}
      span{display:none;}
      table{border-collapse:collapse;margin:10px 0px;}
      caption{text-align:left;font-size:18px;font-weight:bold;margin:10px 0px;}
      td{padding:10px;border:2px solid #ccc;}
      td.bench{color:#c60;font-weight:normal;}
      td.code{font-family:"courier new",monospace;}

    </style>

    <script type="text/javascript">

      var chars = '0123456789abcdef';

      function getRandomString() {
        var len = Math.ceil(Math.random() * 7) + 3; // 4-10
        var result = "";
        while (len--) {
          result += chars.charAt(Math.floor(Math.random() * chars.length));
        }
        return result;
      }

      function createDummySpans(howMany) {
        for (var i=0; i<howMany; i++) {
          var span = document.createElement('span');
          span.innerHTML = ' ' + getRandomString() + ' ';
          document.body.appendChild(span);
        }
      }

      function esc(str) {
        str=str.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
        return str;
      }

      var forEachImp = 'native';
      if (!Array.prototype.forEach) {
        forEachImp = 'custom';
        Array.prototype.forEach = function(fun) {
          var len = this.length;
          if (typeof fun != "function") { throw new TypeError(); }
          var thisp = arguments[1];
          for (var i = 0; i < len; i++) {
            if (i in this) { fun.call(thisp, this[i], i, this); }
          }
        };
      }

      function test() {
        document.getElementById('h').firstChild.data = 'Testing...';
        document.getElementById('h').style.color='#c60';

        var size = !+"\v1" ? 300 : 1000;
        var mult =   100;

        var arr = [];
        var lsize = size;
        while (lsize--) { arr.push('' + getRandomString() + ''); }

        var sarr = [];
        var lsize = size;
        var which_i = 0;
        while (lsize--) {
          var skip_ahead = 0;
          if (Math.random()> 0.4) {
            skip_ahead = Math.ceil(Math.random() * 10);
          } else if (Math.random()> 0.2) {
            skip_ahead = Math.ceil(Math.random() * 50);
          }
          which_i += skip_ahead;
          sarr[which_i] = '' + getRandomString() + '';
        }

        createDummySpans(size);
        var hColl = document.getElementsByTagName('span');

        var start = {};
        var end = {};
        var comment = {};
        var testDesc;
        var result = "";

        //#########################################

        testDesc = "for (var i=0; i<arr.length; i++) {  }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          for (var i=0; i<arr.length; i++) {  }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = '普通for循环';

        //#########################################

        testDesc = "for (var i=0, len=arr.length; i<len; i++) {  }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          for (var i=0, len=arr.length; i<len; i++) {  }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = 'for循环,但预先缓存length属性';

        //#########################################

        testDesc = "var i = 0; while (i < arr.length) { i++; }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          var i = 0; while (i < arr.length) { i++; }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = '普通while循环';

        //#########################################

        testDesc = "var i = 0, len = arr.length; while (i < len) { i++; }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          var i = 0, len = arr.length; while (i < len) { i++; }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = 'while循环,但预先缓存length属性';

        //#########################################

        testDesc = "var i = arr.length; while (i--) {  }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          var i = arr.length; while (i--) {  }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = '倒序while循环';

        //#########################################

        testDesc = "var i = arr.length-1; do {  } while (i--);";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          var i = arr.length-1; do {  } while (i--);
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = 'do ... while循环';

        //#########################################

        testDesc = "for (var i=arr.length; i--;) {  }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          for (var i=arr.length; i--;) {  }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = '倒序for循环';

        //#########################################

        var popArrs = [];
        for (var i=0; i<mult; i++) { popArrs.push(arr.slice()); }
        testDesc = "var x; while (x = arr.pop()) {  }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          var x; while (x = popArrs[a].pop()) {  }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = '通过pop方法驱动while循环(但可能会在稀疏数组中出错).';

        //#########################################

        testDesc = "for (var i in arr) {  }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          for (var i in arr) {  }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = 'for...in循环(但可能会遍历更多东西)';

        //#########################################

        testDesc = "var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in arr) { if(!isInt.test(i)){continue;} }";
        start[testDesc] = new Date().getTime();
        var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/;
        for(var a=0;a<mult;a++) {
          for (var i in arr) { if(!isInt.test(i)){continue;} }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = 'for...in循环并进行isInt检测';

        //#########################################

        testDesc = "for (var i=0; arr[i]; i++) {  }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          for (var i=0; arr[i]; i++) {  }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = 'for循环,利用元素的自动转换进行中断循环(但如果某个元素为零……).';

        //#########################################

        testDesc = "for (var i=0; arr[i]; i++) { var x = arr[i]; }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          for (var i=0; arr[i]; i++) { var x = arr[i]; }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = '同上,并增加更多检测';

        //#########################################

        testDesc = "for (var i=0, x; x = arr[i++];) {  }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          for (var i=0, x; x = arr[i++];) {  }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = '同上,合并当中两个步骤.';

        //#########################################

        testDesc = "for (var i=0, len=arr.length; < len; i++) { var x = arr[i]; }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          for (var i=0, len=arr.length; i<len; i++) { var x = arr[i]; }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = '普通循环,并在循环增加引用';

        //#########################################

        testDesc = "arr.forEach(function(x){});";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          arr.forEach(function(x){});
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = forEachImp+' forEach循环';

        //#########################################

        testDesc = "var f=function(x){}; for (var i=0, len=arr.length; i<len; i++) { f(arr[i]); }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          var f=function(x){}; for (var i=0, len=arr.length; i<len; i++) { f(arr[i]); }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = '用for循环模拟forEach';

        //#########################################

        result += '<table class="data">';
        result += '<caption>原生数组对象 (length='+arr.length+', looped '+mult+' times)</caption>';
        for (var testDesc in start) {
          result += '<tr>';
          result += '<td class="comment">'+comment[testDesc]+'</td>';
          result += '<td class="code">'+esc(testDesc)+'</td>';
          result += '<td class="bench">'+(end[testDesc] - start[testDesc])+'ms</td>';
          result += '</tr>';
        }
        result += '</table>';

        var start = {};
        var end = {};
        var comment = {};

        //#########################################

        testDesc = "for (var i=0; i<sarr.length; i++) {  }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          for (var i=0; i<sarr.length; i++) {  }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = '普通for循环';

        //#########################################

        testDesc = "for (var i=0, len=sarr.length; i<len; i++) {  }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          for (var i=0, len=sarr.length; i<len; i++) {  }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = 'for循环,但预先缓存length属性';

        //#########################################

        testDesc = "var i = 0; while (i < sarr.length) { i++; }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          var i = 0; while (i < sarr.length) { i++; }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = '普通while循环';

        //#########################################

        testDesc = "var i = 0, len = sarr.length; while (i < len) { i++; }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          var i = 0, len = sarr.length; while (i < len) { i++; }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = 'while循环,但预先缓存length属性';

        //#########################################

        testDesc = "var i = sarr.length; while (i--) {  }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          var i = sarr.length; while (i--) {  }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = '倒序while循环';

        //#########################################

        testDesc = "var i = sarr.length-1; do {  } while (i--);";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          var i = sarr.length-1; do {  } while (i--);
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = 'do ... while循环';

        //#########################################

        testDesc = "for (var i=sarr.length; i--;) {  }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          for (var i=sarr.length; i--;) {  }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = '倒序for循环';

        //#########################################

        testDesc = "for (var i in sarr) {  }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          for (var i in sarr) {  }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = 'for...in循环';

        //#########################################

        testDesc = "var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in sarr) { if(!isInt.test(i)){continue;} }";
        start[testDesc] = new Date().getTime();
        var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/;
        for(var a=0;a<mult;a++) {
          for (var i in sarr) { if(!isInt.test(i)){continue;} }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = 'for...in循环并进行isInt检测';

        //#########################################

        testDesc = "sarr.forEach(function(x){});";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          sarr.forEach(function(x){});
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] =forEachImp+ 'Array.forEach() ';

        //#########################################

        testDesc = "var f=function(x){}; for (var i=0, len=sarr.length; i<len; i++) { f(sarr[i]); }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          var f=function(x){}; for (var i=0, len=sarr.length; i<len; i++) { f(sarr[i]); }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = '用for循环模拟forEach';

        //#########################################

        result += '<table class="data">';
        result += '<caption>稀疏数组对象 (length='+sarr.length+', sporadically populated with '+size+' items, looped '+mult+' times)</caption>';
        for (var testDesc in start) {
          result += '<tr>';
          result += '<td class="comment">'+comment[testDesc]+'</td>';
          result += '<td class="code">'+esc(testDesc)+'</td>';
          result += '<td class="bench">'+(end[testDesc] - start[testDesc])+'ms</td>';
          result += '</tr>';
        }
        result += '</table>';

        var start = {};
        var end = {};
        var comment = {};

        //#########################################

        testDesc = "for (var i=0; i<hColl.length; i++) {  }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          for (var i=0; i<hColl.length; i++) {  }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = '普通for循环';

        //#########################################

        testDesc = "for (var i=0, len=hColl.length; i<len; i++) {  }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          for (var i=0, len=hColl.length; i<len; i++) {  }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = 'for循环,但预先缓存length属性';

        //#########################################

        testDesc = "var i = 0; while (i < hColl.length) { i++; }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          var i = 0; while (i < hColl.length) { i++; }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = '普通while循环';

        //#########################################

        testDesc = "var i = 0, len = hColl.length; while (i < len) { i++; }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          var i = 0, len = hColl.length; while (i < len) { i++; }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = 'while循环,但预先缓存length属性';

        //#########################################

        testDesc = "var i = hColl.length; while (i--) {  }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          var i = hColl.length; while (i--) {  }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = '倒序while循环';

        //#########################################

        testDesc = "var i = hColl.length-1; do {  } while (i--);";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          var i = hColl.length-1; do {  } while (i--);
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = 'do ... while循环';

        //#########################################

        testDesc = "for (var i=hColl.length; i--;) {  }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          for (var i=hColl.length; i--;) {  }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = '倒序for循环';

        //#########################################

        testDesc = "for (var i in hColl) {  }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          for (var i in hColl) {  }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = 'for...in循环';

        //#########################################

        testDesc = "var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/; for (var i in hColl) { if(!isInt.test(i)){continue;} }";
        start[testDesc] = new Date().getTime();
        var isInt = /(^[0-9]$)|(^[1-9][0-9]+$)/;
        for(var a=0;a<mult;a++) {
          for (var i in hColl) { if(!isInt.test(i)){continue;} }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = 'for...in循环并进行isInt检测';

        //#########################################

        testDesc = "for (var i=0; hColl[i]; i++) {  }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          for (var i=0; hColl[i]; i++) {  }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = 'for循环,通过自动转换元素来中断循环';

        //#########################################

        testDesc = "for (var i=0; hColl[i]; i++) { var x = hColl[i]; }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          for (var i=0; hColl[i]; i++) { var x = hColl[i]; }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = '同上,并在循环中引用元素';

        //#########################################

        testDesc = "for (var i=0, x; x = hColl[i++];) {  }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          for (var i=0, x; x = hColl[i++];) {  }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = '同上,不过是利用引用中断循环';

        //#########################################

        testDesc = "for (var i=0, x; x = hColl.item(i++);) {  }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          for (var i=0, x; x = hColl.item(i++);) {  }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = '同上,不过是通过item获取元素然后放到变量';

        //#########################################

        testDesc = "for (var i=0, len=hColl.length; i<len; i++) { var x = hColl[i]; }";
        start[testDesc] = new Date().getTime();
        for(var a=0;a<mult;a++) {
          for (var i=0, len=hColl.length; i<len; i++) { var x = hColl[i]; }
        }
        end[testDesc] = new Date().getTime();
        comment[testDesc] = 'for循环,在循环增加引用元素';

        //#########################################

        result += '<table class="data">';
        result += '<caption>HTML Collection对象 (length='+hColl.length+', looped '+mult+' times)</caption>';
        for (var testDesc in start) {
          result += '<tr>';
          result += '<td class="comment">'+comment[testDesc]+'</td>';
          result += '<td class="code">'+esc(testDesc)+'</td>';
          result += '<td class="bench">'+(end[testDesc] - start[testDesc])+'ms</td>';
          result += '</tr>';
        }
        result += '</table>';

        document.body.innerHTML += result;

        document.getElementById('h').firstChild.data = 'Done Testing.';
        document.getElementById('h').style.color='#060';
      }

    </script>

  </head>
  <body>

<h1 id="h">javascript循环测试程序</h1>

<h2><a href="#" onclick="this.style.display='none';test();return false;">开始测试</a></h2>

  </body>
</html>

 

由上面的实验我们基本上可得出以下结论:

  1. while循环比for循环快。
  2. 倒序循环比顺序循环快。
  3. 缓存length属性比不缓存快。
  4. 请不要用arr[i]来判断是否中止循环。
  5. 请不要在循环内声明变量,用它来引用数组元素,若真的想省几个字符,请在循环外声明。
  6. 除非是循环对象的属性,请不要用for...in循环。
  7. 请不要用JavaScript 1.6 的forEach迭代器,特慢!如果一定要用forEach,那么建议自己打造一个。

由于javascript与DOM紧密相连,操作DOM基本上是操作其中的元素节点,因此我觉得基本上以下几个循环就够用:

//顺序
for(var i=0,n=arr.length;i<n;i++){}
  
var i = 0, n= arr.length; while (i<n) { i++; } 
  
//倒序
  
var i = arr.length; while (i--) { } 
  
//遍历对象(不要用在数组上)
var val;
for(var key in obj){
     val = obj[key]
}

 

 

posted @ 2012-07-20 11:00  码写人生  阅读(144)  评论(0)    收藏  举报