写好循环也不容易--8种遍历方法执行速度深度°对比

wbkz6929 7年前
   <p>关于数组或对象遍历,相信很多人都没有深入观察过执行效率。这是一个曾在群里吵翻天的话题,读懂后你将成为遍历效率话题的大师。</p>    <h3>导读</h3>    <p>遍历数组或对象是一名程序员的基本素养之一. 然而遍历却不是一件简单的事, 优秀的程序员知道怎么去选择合适的遍历方法, 优化遍历效率. 本篇将带你走进JavaScript遍历的世界, 享受分析JS循环的快感. 本篇所有代码都可以直接运行, 希望您通读本篇后, 不止是浏览, 最好是亲手去实践下.</p>    <h3>概述</h3>    <p>js有如下两种数据需要经常遍历</p>    <ul>     <li>数组(Array)</li>     <li>对象(Object)</li>    </ul>    <p>同时又提供了如下8种方法方便我们遍历元素</p>    <ul>     <li>for</li>     <li>while(或do~while)</li>     <li>forEach</li>     <li>for in</li>     <li>$.each</li>     <li>$(selecter).each</li>     <li>map</li>     <li>every</li>    </ul>    <p>最终我们将分析遍历效率选出最佳遍历选手.</p>    <p>本文将针对如下两种数据进行详细的分析和举栗. 下面举栗中如果不加特殊说明将会用到如下数据.</p>    <pre>  <code class="language-javascript">var array = ["囚徒","过客","领袖"];//职场3种人  var o = {0:"linda",1:"style",2:"nick",length:3};</code></pre>    <h3>for</h3>    <p>语法: for(初始化; 循环执行条件; 每遍历一个元素后做的事情;){}</p>    <pre>  <code class="language-javascript">(function(){//循环置于闭包之内    for(var i=0,length=array.length;i<length;i++){//缓存数组长度        console.log(array[i]);//内部方法若有可能相互影响,也要置于闭包之内    }  })();</code></pre>    <p>for循环只能遍历数组, 不能遍历对象. 写for循环时有两点需要注意.</p>    <ul>     <li>其一, 为了避免遍历时执行多遍计算数组长度的操作, 影响效率, 建议在循环开始以变量的形式缓存下数组长度, 若在循环内部有可能改变数组长度, 请务必慎重处理, 避免数组越界.</li>     <li>JavaScript中并没有类似java的块级作用域, for循环内部定义的变量会直接暴露在外(如 i,循环退出后,i变量将等于数组长度, 后续代码将能访问到 i 变量的值), 因此建议将for循环置于闭包内. 特别要注意的是: 如果在循环内部, 前一个元素的遍历有可能影响到后一个元素的遍历, 那么for循环内部方法也需要置于闭包之内.</li>    </ul>    <h3>do/while</h3>    <p>语法: do{...}while(true);</p>    <pre>  <code class="language-javascript">do while  (function() {      var i = 0,          len = array.length;      do {          if (i == 2) {              break; // 循环被终止, 此处如果是continue就会造成循环无法退出          };          console.log('array['+ i +']:' + array[i]);          i++;//此句建议放置循环while头部      } while(i<len);  })();</code></pre>    <p>do/while的语法简化了循环的实现, 只保留对循环条件的判断, 所以我们要在循环内部构造出循环退出的条件, 否则有可能造成死循环. 特别要注意的是: 使用 continue 跳出本次遍历时, 要保证循环能够自动进入到下一次遍历, 因此保证循环走到下一次遍历的语句需要放到 continue 前面执行, 建议置于循环头部 .(如上, i++ 语句最好放置循环头部)</p>    <p>do/while 循环与for循环大体差不多,只支持数组遍历, 多用于对循环退出条件不是很明确的场景. 一般来说不建议使用这种方式遍历数组.</p>    <h3>forEach</h3>    <p>语法: array.forEach(function(item){}), 参数item表示数组每一项的元素</p>    <pre>  <code class="language-javascript">array.forEach(function(item){    if(item=="囚徒")      return;//这里只能使用return跳过当前元素处理    console.log(item);  });</code></pre>    <p>forEach回调function默认有三个参数: item, index, array.</p>    <p>使用forEach循环有几点需要特别注意:</p>    <ul>     <li>forEach无法遍历对象</li>     <li>forEach无法在IE中使用,只是在firefox和chrome中实现了该方法</li>     <li>forEach无法使用break,continue跳出循环,使用return时,效果和在for循环中使用continue一致</li>    </ul>    <h3>for in</h3>    <p>语法: for(var item in array){}</p>    <pre>  <code class="language-javascript">for(var item in array){    console.log(item);  }//0 1 2  for(var item in o){    console.log(item);  }//0 1 2 length</code></pre>    <p>for in 可用于遍历数组和对象, 但它输出的只是数组的索引和对象的key, 我们可以通过索引和key取到对应的值. 如下:</p>    <pre>  <code class="language-javascript">for(var item in array){    console.log(array[item]);  }//"囚徒" "过客" "领袖"  for(var item in o){    console.log(o[item]);  }//"linda" "style" "nick" "length"</code></pre>    <h3>$.each</h3>    <p>语法: $.each(array|o, function(i, ele){}) 支持数组和对象</p>    <pre>  <code class="language-javascript">$.each(array, function(i, ele){    console.log(i,ele,this==ele);  });  //0 "囚徒" true  //1 "过客" true  //2 "领袖" true  $.each(o, function(i, ele){    console.log(i,ele,this==ele);  });  //0 "linda" true  //1 "style" true  //2 "nick" true</code></pre>    <p>这里我们注意到 <strong>this对象 指向当前属性的值</strong> ,这是因为:</p>    <p>参考jQuery api:</p>    <p>$.each() 方法会迭代jQuery对象中的每一个DOM元素。每次回调函数执行时,会传递当前循环次数作为参数(从0开始计数)。更重要的是,回调函数是在当前DOM元素为上下文的语境中触发的。因此关键字 this 总是指向这个元素。</p>    <p>同时,上述遍历时, o 对象的属性中有一个length属性并没有被输出. 这是为什么呢? 请耐心往下看.</p>    <p>首先, 我们来看看遍历对象o时, 当前的this对象到底是什么?</p>    <pre>  <code class="language-javascript">$.each(o, function(i, ele){    if(this=="linda"){//我们随机选取第一个属性      console.log(this,this==ele);      $.each(this, function(e, ele2) {          console.log(e, ele2);      });    }  });  //String {0: "l", 1: "i", 2: "n", 3: "d", 4: "a", length: 5, [[PrimitiveValue]]: "linda"} true  //0 "l"  //1 "i"  //2 "n"  //3 "d"  //4 "a"</code></pre>    <p>我们发现, this对象等于回调函数的第二个形参. 且它的 length 属性和 [[PrimitiveValue]] 属性并没有被打印出来, 为此我们来查看下length的内部属性.</p>    <pre>  <code class="language-javascript">$.each(o, function(i, ele){    if(this=="linda")//我们还是随机选取第一个属性(这还是随机吗?)      console.log(Object.getOwnPropertyDescriptor(this, 'length'));  });  //Object {value: 5, writable: false, enumerable: false, configurable: false}</code></pre>    <p>可见, this对象的length属性的 enumerable 属性被设置成了false, 这表示该对象不能被列举或遍历, 同时还不能被配置(configurable: false), 也不能被赋值(writable: false).</p>    <p>此时, 前面遍历 o 对象时,它的 length 属性没有被打印出来的疑问似乎有解了. 让我们来看看 o.length 的内部属性吧.</p>    <pre>  <code class="language-javascript">console.log(Object.getOwnPropertyDescriptor(o, 'length'));  //Object {value: 3, writable: true, enumerable: true, configurable: true}</code></pre>    <p>o.length 值为3, 可赋值, 可列举, 可配置. 这可不对, 刚刚不是说 enumerable 属性被设置成了false 才不会被遍历吗. 现在该值为 true, 并且还不可遍历. 这不合常理, 自然该有别的原因. 我们接着往下看.</p>    <pre>  <code class="language-javascript">var o = {0:"linda",1:"style",2:"nick",length:1}; // 试着改变length的值  $.each(o, function(i, ele){//再遍历一次    console.log(i,ele);  });  //0 "linda"    var o = {0:"linda",1:"style",2:"nick",length:5}; // 坚持改变length的值  $.each(o, function(i, ele){//再遍历一次    console.log(i,ele);  });  // 0 linda  // 1 style  // 2 nick  // length 5    var o = {0:"linda",1:"style",2:"nick"}; // 试试去掉length属性  $.each(o, function(i, ele){//再遍历一次    console.log(i,ele);  });  // 0 linda  // 1 style  // 2 nick</code></pre>    <p>现象明了, 结合jquery源码, 当对象中存在length属性时, $.each 内部使用for循环去遍历对象, 否则它将使用for in循环去遍历, 因此$.each遍历对象遵循如下规律:</p>    <ul>     <li>如果对象中存在 length 属性, 遍历深度以length属性为准, 即length多大, 遍历多少个元素.</li>     <li>如果对象中不存在 length 属性, 遍历深度以实际内部属性个数为准.</li>    </ul>    <p>不仅如此, $.each的具体使用过程中还有以下几点需要注意:</p>    <ul>     <li>使用 return 或者 return true 为跳过一个元素,继续执行后面的循环;</li>     <li>使用 return false 为终止循环的执行, 这是因为在 jquery.each 中, 若返回值指定为false, 才跳出循环, 如果感兴趣请翻看 jquery.each 源码;</li>     <li>无法使用 break 与 continue 来跳过循环.</li>    </ul>    <h3>$(selecter).each</h3>    <p>语法: $(selecter|array|o).each(function(i, ele){}) 支持数组和对象, 该方法基本上与$.each方法相同.</p>    <pre>  <code class="language-javascript">$('div').each(function(i,ele){    console.log(this,i,this == ele);  });  //dom... 0 dom.... true  $(array).each(function(i,ele){//处理数组    if(this == "领袖")        console.log(this,i,this == ele);  });  //String {0: "领", 1: "袖", length: 2, [[PrimitiveValue]]: "领袖"} 2 true  $(o).each(function(i,ele){//处理对象    if(this == "nick")        console.log(this,i,this == ele);  });  //String {0: "n", 1: "i", 2: "c", 3: "k", length: 4, [[PrimitiveValue]]: "nick"} 2 true</code></pre>    <p>dom表示div元素, 由于this恒等ele, 说明this也表示div元素, 所以this并不是jquery对象, 而是普通的 <a href="/misc/goto?guid=4959747932907071992" rel="nofollow,noindex"> DOM对象 </a> (可以在this上随意使用DOM方法). 使用$(selecter).each方法,请注意以下几点:</p>    <ul>     <li>i: 即序列值 ele: 表示当前被遍历的DOM元素</li>     <li>this 表示当前被遍历的DOM元素,不能调用jQuery方法, 如需调用jquery方法需要用$符号包裹.如, $(this)</li>    </ul>    <h3>map</h3>    <p>即 Array.prototype.map,该方法只支持数组</p>    <p>语法: array.map(callback[,thisArg]) map方法使用其提供函数的每次返回结果生成一个新的数组.</p>    <pre>  <code class="language-javascript">var array = [1, 4, 9];  var roots = array.map(Math.sqrt);//map包裹方法名  // roots is now [1, 2, 3], array is still [1, 4, 9]  var array = [1, 4, 9];  var doubles = array.map(function(num) {//map包裹方法实体    return num * 2;  });  // doubles is now [2, 8, 18]. array is still [1, 4, 9]</code></pre>    <p>实际上,由于map方法被设计成支持 [ 鸭式辨型 ][] , 该方法也可以用来处理形似数组的对象, 例如 NodeList.</p>    <pre>  <code class="language-javascript">var elems = document.querySelectorAll('select option:checked');  var values = Array.prototype.map.call(elems, function(obj) {    return obj.value;  });</code></pre>    <p>甚至还可以用来处理字符串, 如下:</p>    <pre>  <code class="language-javascript">var map = Array.prototype.map;  var array = map.call('Hello 中国', function(x) {     return x.charCodeAt(0);  });  console.log(array);  //[72, 101, 108, 108, 111, 32, 20013, 22269]</code></pre>    <p>map处理字符串的方式多种多样, 例如 反转等.</p>    <pre>  <code class="language-javascript">var str = '12345';  var output = Array.prototype.map.call(str, function(x) {    return x;  }).reverse().join('');  console.log(output);//54321</code></pre>    <p>例如 将字符串数组转换为数字数组, 只需一条语句, 如下:</p>    <pre>  <code class="language-javascript">console.log(['1', '2', '3'].map(Number));//[1,2,3]</code></pre>    <p>目前map方法被大部分浏览器支持, 除了IE 6,7,8.</p>    <h3>every</h3>    <p>即 Array.prototype.every, 该方法同上述map方法也只支持数组</p>    <p>语法: arr.every(callback[, thisArg]) every 方法用于检验数组中的每一项是否符合某个条件, 若符合则放回true, 反之则返回false.</p>    <pre>  <code class="language-javascript">function isBigEnough(element, index, array) {    return element >= 10;  }  [12, 5, 8, 130, 44].every(isBigEnough);   // false  [12, 54, 18, 130, 44].every(isBigEnough); // true</code></pre>    <p>该方法还有简写方式, 如下:</p>    <pre>  <code class="language-javascript">[12, 5, 8, 130, 44].every(elem => elem >= 10); // false  [12, 54, 18, 130, 44].every(elem => elem >= 10); // true</code></pre>    <p>以上, 遍历数组和对象的8种方法简单的介绍完, 小结如下:</p>    <ul>     <li>for in , $.each , $().each 既支持对象也支持数组遍历;</li>     <li>for , do/while , forEach 只支持数组;</li>     <li>Array.prototype.map, Array.prototype.every 只支持数组和形似数组的对象;</li>     <li>forEach不能退出循环,只能通过return来进入到下一个元素的遍历中(相当于for循环的continue), 且在IE没有实现该方法;</li>     <li>$.each和$().each循环只能通过return false 来退出循环, 使用return 或 return true 将跳过一个元素, 继续执行后面的循环.</li>    </ul>    <h3>测试各方法效率</h3>    <p>下面我们来测试下上述方法的效率.</p>    <p>注: array数组默认为空, 依次赋值数组长度为1 000 000, 10 000 000, 100 000 000, 分别在 Chrome, Firefox, Safari 浏览器上进行两轮测试, 取测试时间平均值作为比较对象, 时间单位为ms. 如下是测试代码:</p>    <pre>  <code class="language-javascript">var array = [],      length = array.length = 10000000;//(一千万)  //for(var i=0;i<length;i++){  //  array[i] = 'louis';  //}  console.log(array[0]);  //-------------------------for  var t1 = +new Date();  for(var i=0;i<length;i++){  }  var t2 = +new Date();  console.log('for:' + (t2-t1));    //-------------------------do/while  var t1 = +new Date();  var i = 0;  do {      i++;  } while(i<length);  var t2 = +new Date();  console.log('do while:' + (t2-t1));    //-------------------------forEach  var t1 = +new Date();  array.forEach(function(item){  });  var t2 = +new Date();  console.log('forEach:' + (t2-t1));    //-------------------------for in  var t1 = +new Date();  for(var item in array){  }  var t2 = +new Date();  console.log('for in:' + (t2-t1));    //------------------------- $.each  var t1 = +new Date();  $.each(array, function(i, ele){  });  var t2 = +new Date();  console.log('$.each:' + (t2-t1));    //-------------------------$().each  var t1 = +new Date();  $(array).each(function(i,ele){  });  var t2 = +new Date();  console.log('$(ele).each:' + (t2-t1));    //-------------------------map  var t1 = +new Date();  array.map(function(num){  });  var t2 = +new Date();  console.log('map:' + (t2-t1));    //-------------------------every  var t1 = +new Date();  array.every(function(e,i,arr){  });  var t2 = +new Date();  console.log('every:' + (t2-t1));</code></pre>    <p>测试机器正常运行 IDE, 编辑器, 浏览器, qq, 微信等常用应用, 系统空闲. 硬件设备如下:</p>    <ul>     <li>操作系统: OSX EI Capitan 版本 10.11.5</li>     <li>MacBook Pro(13 英寸,2015 年初期)</li>     <li>处理器: 2.7 GHz Intel Core i5</li>     <li>内存: 8 GB 1867 MHz DDR3</li>    </ul>    <p>以上多轮测试结果汇总如下三张表(单位:ms):</p>    <p>数组长度为10^6</p>    <table>     <thead>      <tr>       <th>数组长度为10^6</th>       <th>chrome 52.0.2743.116 (64-bit)</th>       <th>Firefox Developer Edition 49.0a2 (2016-08-01)</th>       <th>Safari 9.1.1 (11601.6.17)</th>      </tr>     </thead>     <tbody>      <tr>       <td>for</td>       <td>(16+19)/2 = 17.5</td>       <td>(6+7)/2 = 6.5</td>       <td>(6+7)/2 = 6.5</td>      </tr>      <tr>       <td>do while</td>       <td>(24+17)/2 = 20.5</td>       <td>(7+5)/2 = 6</td>       <td>(5+5)/2 = 5</td>      </tr>      <tr>       <td>for in</td>       <td>(19+28)/2 = 23.5</td>       <td>(0+0)/2 = 0</td>       <td>(0+0)/2 = 0</td>      </tr>      <tr>       <td>forEach</td>       <td>(41+28)/2 = 34.5</td>       <td>(4+4)/2 = 4</td>       <td>(31+29)/2 = 30</td>      </tr>      <tr>       <td>map</td>       <td>(26+32)/2 = 28</td>       <td>(4+4)/2 = 4</td>       <td>(32+26)/2 = 28</td>      </tr>      <tr>       <td>every</td>       <td>(22+24)/2 = 23</td>       <td>(4+5)/2 = 4.5</td>       <td>(41+45)/2 = 43</td>      </tr>      <tr>       <td>$.each</td>       <td>(29+27)/2 = 28</td>       <td>(306+311)/2 = 308.5</td>       <td>(111+97)/2 = 104</td>      </tr>      <tr>       <td>$(e).each</td>       <td>(94+98)/2 = 96</td>       <td>(484+488)/2 = 486</td>       <td>(79+64)/2 = 71.5</td>      </tr>     </tbody>    </table>    <p>数组长度为10^7</p>    <table>     <thead>      <tr>       <th>数组长度为10^7</th>       <th>chrome 52.0.2743.116 (64-bit)</th>       <th>Firefox Developer Edition 49.0a2 (2016-08-01)</th>       <th>Safari 9.1.1 (11601.6.17)</th>      </tr>     </thead>     <tbody>      <tr>       <td>for</td>       <td>(164+161)/2 = 162.5</td>       <td>(26+30)/2 = 28</td>       <td>(30+31)/2 = 30.5</td>      </tr>      <tr>       <td>do while</td>       <td>(163+157)/2 = 160</td>       <td>(27+25)/2 = 26</td>       <td>(28+27)/2 = 27.5</td>      </tr>      <tr>       <td>for in</td>       <td>(78+86)/2 = 82</td>       <td>(0+0)/2 = 0</td>       <td>(0+0)/2 = 0</td>      </tr>      <tr>       <td>forEach</td>       <td>(211+205)/2 = 208</td>       <td>(31+30)/2 = 30.5</td>       <td>(291+289)/2 = 290</td>      </tr>      <tr>       <td>map</td>       <td>(349+282)/2 = 315.5</td>       <td>(24+22)/2 = 23</td>       <td>(259+260)/2 = 259.5</td>      </tr>      <tr>       <td>every</td>       <td>(221+219)/2 = 220</td>       <td>(24+24)/2 = 24</td>       <td>(251+257)/2 = 254</td>      </tr>      <tr>       <td>$.each</td>       <td>(210+215)/2 = 212.5</td>       <td>(2868+2789)/2 = 2828.5</td>       <td>(699+724)/2 = 711.5</td>      </tr>      <tr>       <td>$(e).each</td>       <td>(730+669)/2 = 699.5</td>       <td>(4674+4722)/2 = 4698</td>       <td>(523+546)/2 = 534.5</td>      </tr>     </tbody>    </table>    <p>数组长度为10^8</p>    <table>     <thead>      <tr>       <th>数组长度为10^8</th>       <th>chrome 52.0.2743.116 (64-bit)</th>       <th>Firefox Developer Edition 49.0a2 (2016-08-01)</th>       <th>Safari 9.1.1 (11601.6.17)</th>      </tr>     </thead>     <tbody>      <tr>       <td>for</td>       <td>(1486+1583)/2 = 1534.5</td>       <td>(222+238)/2 = 230</td>       <td>(261+251)/2 = 256</td>      </tr>      <tr>       <td>do while</td>       <td>(1548+1608)/2 = 1578</td>       <td>(236+247)/2 = 241.5</td>       <td>(272+265)/2 = 268.5</td>      </tr>      <tr>       <td>for in</td>       <td>(0+0)/2 = 0</td>       <td>(0+0)/2 = 0</td>       <td>(0+0)/2 = 0</td>      </tr>      <tr>       <td>forEach</td>       <td>(25838+22307)/2 = 24072.5</td>       <td>(212+209)/2 = 210.5</td>       <td>(2565+2568)/2 = 2566.5</td>      </tr>      <tr>       <td>map</td>       <td>(23795+22787)/2 = 23291</td>       <td>(215+206)/2 = 210.5</td>       <td>(2556+2573)/2 = 2564.5</td>      </tr>      <tr>       <td>every</td>       <td>(22393+22378)/2 = 22385.5</td>       <td>(212+215)/2 = 213.5</td>       <td>(2550+2548)/2 = 2549</td>      </tr>      <tr>       <td>$.each</td>       <td>(14523+14776)/2 = 14649.5</td>       <td>(28007+27698)/2 = 27852.5</td>       <td>(7109+7156)/2 = 7132.5</td>      </tr>      <tr>       <td>$(e).each</td>       <td>chrome 奔溃了...</td>       <td>(49352+49530)/2 = 49441</td>       <td>(5505+4616)/2 = 5060.5</td>      </tr>     </tbody>    </table>    <p>综上, 我们发现for in 循环的性能不稳定, 猜测它可能没有进入循环. 因此将数组各元素进行如下赋值. 重新进行如下两轮测试.</p>    <pre>  <code class="language-javascript">var array = [],      length = array.length = 1000000;  for(var i=0;i<length;i++){    array[i] = 'louis';  }</code></pre>    <p>数组赋值后, 数组长度为10^6</p>    <table>     <thead>      <tr>       <th>数组长度为10^6</th>       <th>chrome 52.0.2743.116 (64-bit)</th>       <th>Firefox Developer Edition 49.0a2 (2016-08-01)</th>       <th>Safari 9.1.1 (11601.6.17)</th>      </tr>     </thead>     <tbody>      <tr>       <td>for</td>       <td>(21+22)/2 = 21.5</td>       <td>(8+10)/2 = 9</td>       <td>(6+5)/2 = 5.5</td>      </tr>      <tr>       <td>do while</td>       <td>(22+19)/2 = 20.5</td>       <td>(6+6)/2 = 6</td>       <td>(6+5)/2 = 5.5</td>      </tr>      <tr>       <td>for in</td>       <td>(178+184)/2 = 181</td>       <td>(318+268)/2 = 293</td>       <td>(413+464)/2 = 438.5</td>      </tr>      <tr>       <td>forEach</td>       <td>(42+45)/2 = 43.5</td>       <td>(4+4)/2 = 4</td>       <td>(21+24)/2 = 22.5</td>      </tr>      <tr>       <td>map</td>       <td>(137+153)/2 = 145</td>       <td>(9+8)/2 = 8.5</td>       <td>(38+43)/2 = 40.5</td>      </tr>      <tr>       <td>every</td>       <td>(0+0)/2 = 0</td>       <td>(0+0)/2 = 0</td>       <td>(0+0)/2 = 0</td>      </tr>      <tr>       <td>$.each</td>       <td>(85+84)/2 = 84.5</td>       <td>(15+19)/2 = 17</td>       <td>(37+25)/2 = 31</td>      </tr>      <tr>       <td>$(e).each</td>       <td>(81+83)/2 = 82</td>       <td>(34+31)/2 = 32.5</td>       <td>(37+46)/2 = 41.5</td>      </tr>     </tbody>    </table>    <p>数组赋值后, 数组长度为10^7</p>    <table>     <thead>      <tr>       <th>数组长度为10^7</th>       <th>chrome 52.0.2743.116 (64-bit)</th>       <th>Firefox Developer Edition 49.0a2 (2016-08-01)</th>       <th>Safari 9.1.1 (11601.6.17)</th>      </tr>     </thead>     <tbody>      <tr>       <td>for</td>       <td>(171+157)/2 = 164</td>       <td>(27+26)/2 = 26.5</td>       <td>(26+28)/2 = 27</td>      </tr>      <tr>       <td>do while</td>       <td>(168+158)/2 = 163</td>       <td>(27+27)/2 = 27</td>       <td>(28+29)/2 = 28.5</td>      </tr>      <tr>       <td>for in</td>       <td>(1469+1715)/2 = 1592</td>       <td>(2922+3123)/2 = 3022.5</td>       <td>(5755+5742)/2 = 5748.5</td>      </tr>      <tr>       <td>forEach</td>       <td>(347+329)/2 = 338</td>       <td>(32+36)/2 = 34</td>       <td>(171+174)/2 = 172.5</td>      </tr>      <tr>       <td>map</td>       <td>(1320+1335)/2 = 1327.5</td>       <td>(147+137)/2 = 142</td>       <td>(448+469)/2 = 458.5</td>      </tr>      <tr>       <td>every</td>       <td>(0+0)/2 = 0</td>       <td>(0+0)/2 = 0</td>       <td>(0+0)/2 = 0</td>      </tr>      <tr>       <td>$.each</td>       <td>(438+441)/2 = 439.5</td>       <td>(142+141)/2 = 141.5</td>       <td>(254+248)/2 = 251</td>      </tr>      <tr>       <td>$(e).each</td>       <td>(876+935)/2 = 905.5</td>       <td>(315+328)/2 = 321.5</td>       <td>(450+402)/2 = 426</td>      </tr>     </tbody>    </table>    <p>可见, 对数组进行赋值后, 代码运行基本稳定.(every还不清楚为什么执行时间为0.欢迎大神告知原因.)</p>    <h3>分析总结</h3>    <p>通过以上 30 次运行测试(实际上为了得到比较稳定的数据, 摈弃了许多异常的测试数据), 我们发现在数组长度为10^6, 10^7, 10^8 时, 代码运行基本稳定. 各方法运行需要的时间大致排序如下:</p>    <p>for ~= do while < forEach ~= map ~= every < $.each < $(e).each < for in</p>    <p>根据统计数据, 可得这8个方法的运行速度大致排序为:</p>    <ol>     <li>for 与 do while</li>     <li>forEach map every (这3个不相上下,可认为运行速度差不多)</li>     <li>$.each</li>     <li>$(e).each</li>     <li>for in</li>    </ol>    <p>我们翻看jquery代码就会知道, $.each方法内部通过调用for循环来实现, 而$().each是先用jquery包裹数组对象, 然后再调用for循环, 因此后者效率略低于前者.</p>    <p>综上, 最佳遍历选手是 for/do while循环, 推荐大家优先考虑使用它. ( Firefox浏览器由于对forEach循环做了底层优化, 效率接近native,不在我们考虑范围内 ).</p>    <p>基于测试结果的两点思考</p>    <ol>     <li>从测试数据上猜测, Firefox 与 Safari 似乎对于 for, do while 等都进行了底层优化. 循环执行效率明显优于Chrome.</li>     <li>每次浏览器执行到 for in 循环处, 便会出现卡顿, 猜测浏览器可能正在预加载循环所需资源(后续我将专门分析此处).</li>    </ol>    <p> </p>    <p>参考文章</p>    <ul>     <li><a href="/misc/goto?guid=4959747932999704904" rel="nofollow,noindex">JavaScript 各种遍历方式详解,有你不知道的黑科技</a></li>     <li><a href="/misc/goto?guid=4959747933081577574" rel="nofollow,noindex">js,jquery遍历数组,对象</a></li>     <li><a href="/misc/goto?guid=4959747933165404160" rel="nofollow,noindex">Array.prototype.map()</a></li>     <li><a href="/misc/goto?guid=4959747933247640477" rel="nofollow,noindex">Array.prototype.every()</a></li>    </ul>    <p> </p>    <p>来自:https://juejin.im/post/58fd414c1b69e600589d4568</p>    <p> </p>