在JavaScript中循环数组的最快方法是什么?

won*_*ng2 233 javascript performance loops

我从书中学到了你应该写这样的循环:

for(var i=0, len=arr.length; i < len; i++){
    // blah blah
}
Run Code Online (Sandbox Code Playgroud)

所以arr.length每次都不会计算.

其他人说编译器会对此做一些优化,所以你可以写:

for(var i=0; i < arr.length; i++){
    // blah blah
}
Run Code Online (Sandbox Code Playgroud)

我只是想知道哪种是最好的练习方式?

jon*_*ohn 318

用大多数现代浏览器执行此测试后......

http://jsben.ch/y3SpC

目前,最快的循环形式(在我看来,语法最明显).

带有长度缓存的循环标准

for (var i = 0, len = myArray.length; i < len; i++) {

}
Run Code Online (Sandbox Code Playgroud)

我想说这肯定是我鼓掌JavaScript引擎开发人员的情况.应该优化运行时间以保持清晰,而不是聪明.

  • 你必须小心使用这个循环.我开始使用它并且因为我犯了一个错误而难以追踪错误.如果你嵌套这样的两个循环:http://jsfiddle.net/KQwmL/1/.您必须小心在两个循环中以不同的方式命名var len,否则第二个循环将覆盖第一个len. (20认同)
  • 有趣的是,在IE9中这更快:for(var i = 0,len = myArray.length; i <len; ++ i){} //前缀incr,而不是postfix (6认同)
  • @WillshawMedia您可以使用单个`var`语句声明多个变量.如何写,`len`实际上是你提出的范围. (6认同)
  • 有关使用`++ i`的其他原因,请参阅[首选前缀运算符over postfix](http://thunderguy.com/semicolon/2002/08/13/prefer-prefix-operators-over-postfix/). (4认同)
  • 我使用前缀运算符测试@BennettMcElwee建议它运行得更快:`for(var i = 0,len = myArray.length; i <len; ++ i)`检查http://jsperf.com/caching-阵列长度/ 84 (4认同)
  • @Spencer你错了.来源:http://stackoverflow.com/a/7252102/555384好的,但是老实说,这个答案基于的博客现在已经有6年了,你不认为JS引擎的性能特征有所改变吗?这是我们这个时代的伟大军备竞赛. (2认同)
  • http://jsben.ch/cMAc2改进了测试用例,添加了forEach进行比较 (2认同)
  • @TharinduThisarasinghe 这将永远有效。任何语言中都不存在更有效的表达方式。从技术上讲,++i(使用前缀运算符)更快,因为它使计算机免于复制整数(请注意,对于如此小的整数,这会产生影响)。在回答有关地图的问题时,功能模式几乎总是较慢。对于地图,必须复制所有对象。像 forEach 这样的迭代本身比 for 或 while 循环慢得多。创建函数闭包和复制参数也会减慢其使用速度。 (2认同)

gnu*_*nur 87

循环遍历javascript数组的绝对最快方法是:

var len = arr.length;
while (len--) {
    // blah blah
}
Run Code Online (Sandbox Code Playgroud)

有关完整比较,请参见http://blogs.oracle.com/greimer/entry/best_way_to_code_a

  • 这个答案基于的博客文章已经快4年了,当时js引擎发生了很多变化,请参阅下面的答案以获得更新的比较. (22认同)
  • 我猜@jondavidjohn,你的意思是'我的答案在下面'是'我的答案在上面'大声笑. (3认同)
  • 我同意@jondavidjohn。我测试了这段代码,结果证明效率较低......检查jsperf.com/caching-array-length/84 (2认同)
  • 上面的答案几乎普遍(跨浏览器)比 for 循环慢得多。请参阅已接受答案中的 JSPerf 链接。这是一个巨大的耻辱,因为它非常易读 IMO。 (2认同)

CGo*_*odo 36

截至2016年6月,在最新的Chrome中进行一些测试(2016年5月浏览器市场占71%,并且还在增加):

  • 最快的循环是for循环,无论是否有缓存长度,都能提供非常相似的性能.(具有缓存长度的for循环有时会提供比没有缓存的for循环更好的结果,但差异几乎可以忽略不计,这意味着引擎可能已经过优化以支持标准,并且可能最简单的循环而没有缓存).
  • 减量的while循环比for循环慢大约1.5倍.
  • 使用回调函数(如标准forEach)的循环比for循环慢大约10倍.

我相信这个线程太老了,误导程序员认为他们需要缓存长度,或者使用带有减量的反向遍历来实现更好的性能,编写代码比简单直接的for循环更不易读,更容易出错.因此,我建议:

  • 如果您的应用程序迭代了很多项目,或者您的循环代码位于经常使用的函数中,那么直接的for循环就是答案:

    for (var i = 0; i < arr.length; i++) {
      // Do stuff with arr[i] or i
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 如果您的应用程序没有真正遍历大量项目,或者您只需要在这里和那里进行小的迭代,那么使用标准forEach回调或您选择的JS库中的任何类似函数可能更容易理解并且更不容易出错.索引变量范围已关闭,您不需要使用括号,直接访问数组值:

    arr.forEach(function(value, index) {
      // Do stuff with value or index
    });
    
    Run Code Online (Sandbox Code Playgroud)
  • 如果您在迭代数十亿行时确实需要花几毫秒时间,并且数组的长度不会在整个过程中发生变化,您可以考虑在for循环中缓存长度.虽然我认为现在真的没有必要:

    for (var i = 0, len = arr.length; i < len; i++) {
      // Do stuff with arr[i]
    }
    
    Run Code Online (Sandbox Code Playgroud)


Fel*_*ing 31

如果订单不重要,我更喜欢这种风格:

for(var i = array.length; i--; )
Run Code Online (Sandbox Code Playgroud)

它可以缓存长度,写入时间要短得多.但它会以相反的顺序迭代数组.

  • @MarwaAhmad:不。“i--”返回一个数字,一旦数字为“0”,则条件为“false”,因为“Boolean(0) === false”。 (7认同)
  • 您刚刚杀死了它。 (5认同)

nul*_*ube 24

它只是2018年所以更新可能会很好......

而且我真的不同意接受的答案.它在不同的浏览器上推迟.有些做得forEach更快,有些for-loop,while 这里有一些是所有方法http://jsben.ch/mW36e的基准

arr.forEach( a => {
  // ...
}
Run Code Online (Sandbox Code Playgroud)

因为你可以看到很多for-loop for(a = 0; ... )然后值得一提的是,没有'var'变量将被全局定义,这会极大地影响速度,所以它会变慢.

var arr = arr = new Array(11111111).fill(255);
var benches =     
[ [ "empty", () => {
  for(var a = 0, l = arr.length; a < l; a++);
}]
, ["for-loop", () => {
  for(var a = 0, l = arr.length; a < l; ++a)
    var b = arr[a] + 1;
}]
, ["for-loop++", () => {
  for(var a = 0, l = arr.length; a < l; a++)
    var b = arr[a] + 1;
}]
, ["for-loop - arr.length", () => {
  for(var a = 0; a < arr.length; ++a )
    var b = arr[a] + 1;
}]
, ["reverse for-loop", () => {
  for(var a = arr.length - 1; a >= 0; --a )
    var b = arr[a] + 1;
}]
,["while-loop", () => {
  var a = 0, l = arr.length;
  while( a < l ) {
    var b = arr[a] + 1;
    ++a;
  }
}]
, ["reverse-do-while-loop", () => {
  var a = arr.length - 1; // CAREFUL
  do {
    var b = arr[a] + 1;
  } while(a--);   
}]
, ["forEach", () => {
  arr.forEach( a => {
    var b = a + 1;
  });
}]
, ["for const..in (only 3.3%)", () => {
  var ar = arr.slice(0,arr.length/33);
  for( const a in ar ) {
    var b = a + 1;
  }
}]
, ["for let..in (only 3.3%)", () => {
  var ar = arr.slice(0,arr.length/33);
  for( let a in ar ) {
    var b = a + 1;
  }
}]
, ["for var..in (only 3.3%)", () => {
  var ar = arr.slice(0,arr.length/33);
  for( var a in ar ) {
    var b = a + 1;
  }
}]
, ["Duff's device", () => {
  var i = 0;
  var r = arr.length % 8;
  var n = (arr.length - r) / 8;
  if (r > 0) do {
      var b = arr[i++] + 1;
    }
    while (--r);
  if (n > 0) do {
      var b = arr[i] + 1;
      var c = arr[i+1] + 1;
      var d = arr[i+2] + 1;
      var e = arr[i+3] + 1;
      var f = arr[i+4] + 1;
      var g = arr[i+5] + 1;
      var h = arr[i+6] + 1;
      var k = arr[i+7] + 1;
      i = --n >>> 3;
    }
    while (n);
}]
, ["Duff's device negative", () => {
  var r = arr.length % 8;
  var n = (arr.length-r) / 8; ///Math.floor(arr.length / 8);
  var i	= arr.length ; // -1;

  while(r){
    var b = arr[--i] + 1;
    --r;
  }

  while(n){
      var b = arr[i] + 1;
      var c = arr[i-1] + 1;
      var d = arr[i-2] + 1;
      var e = arr[i-3] + 1;
      var f = arr[i-4] + 1;
      var g = arr[i-5] + 1;
      var h = arr[i-6] + 1;
      var j = arr[i-7] + 1;
      i = --n >>> 3;
  }
}]];
function bench(title, f) {
  var t0 = performance.now();
  var res = f();
  return performance.now() - t0; // console.log(`${title} took ${t1-t0} msec`);
}
var globalVarTime = bench( "for-loop without 'var'", () => {
  // Here if you forget to put 'var' so variables'll be global
  for(a = 0, l = arr.length; a < l; ++a)
     var b = arr[a] + 1;
});
var times = benches.map( function(a) {
                      arr = new Array(11111111).fill(255);
                      return [a[0], bench(...a)]
                     }).sort( (a,b) => a[1]-b[1] );
var max = times[times.length-1][1];
times = times.map( a => {a[2] = (a[1]/max)*100; return a; } );
var template = (title, time, n) =>
  `<div>` +
    `<span>${title} &nbsp;</span>` +
    `<span style="width:${3+n/2}%">&nbsp;${Number(time.toFixed(3))}msec</span>` +
  `</div>`;

var strRes = times.map( t => template(...t) ).join("\n") + 
            `<br><br>for-loop without 'var' ${globalVarTime} msec.`;
var $container = document.getElementById("container");
$container.innerHTML = strRes;
Run Code Online (Sandbox Code Playgroud)
body { color:#fff; background:#333; font-family:helvetica; }
body > div > div {  clear:both   }
body > div > div > span {
  float:left;
  width:43%;
  margin:3px 0;
  text-align:right;
}
body > div > div > span:nth-child(2) {
  text-align:left;
  background:darkorange;
  animation:showup .37s .111s;
  -webkit-animation:showup .37s .111s;
}
@keyframes showup { from { width:0; } }
@-webkit-keyframes showup { from { width:0; } }
Run Code Online (Sandbox Code Playgroud)
<div id="container"> </div>
Run Code Online (Sandbox Code Playgroud)

  • @Maykonn你可能想说"它可以在任何地方工作,但Opera Mini" (3认同)
  • @Maykonn默认视图中未列出,因为0.18%的用户拥有IE8,您不应该浪费时间来支持它; 在2018年,它是一匹死马. (3认同)

coc*_*cco 20

2014年While又回来了

只是思考逻辑.

看这个

for( var index = 0 , length = array.length ; index < length ; index++ ) {

 //do stuff

}
Run Code Online (Sandbox Code Playgroud)
  1. 需要创建至少2个变量(索引,长度)
  2. 需要检查索引是否小于长度
  3. 需要增加指数
  4. 所述for环具有3个参数

现在告诉我为什么这应该比以下更快:

var length = array.length;

while( --length ) { //or length--

 //do stuff

}
Run Code Online (Sandbox Code Playgroud)
  1. 一个变量
  2. 没有检查
  3. 指数下降(机器更喜欢)
  4. while 只有一个参数

当Chrome 28显示for循环比while更快时,我完全感到困惑.这必须有某种程度

"呃,每个人都在使用for循环,让我们在开发chrome时专注于它."

但是现在,在2014年,while循环重新开始使用chrome.它的速度提高了2倍,在其他/旧版浏览器上它总是更快.

最近我做了一些新的测试.现在在现实世界中,那些短代码是没有价值的,而jsperf实际上无法正确执行while循环,因为它需要重新创建array.length,这也需要时间.

你不能在jsperf上获得while循环的实际速度.

你需要创建自己的自定义功能并检查 window.performance.now()

是的......没有办法让while循环更快.

真正的问题实际上是dom操纵/渲染时间/绘图时间,或者你想要调用它.

例如,我有一个画布场景,我需要计算坐标和碰撞......这是在10-200 MicroSeconds(而不是毫秒)之间完成的.它实际上需要几毫秒才能呈现所有内容.如DOM中那样.

loop在某些情况下,还有另一种使用for的超级高效方式...例如复制/克隆数组

for(
 var i = array.length ;
 i > 0 ;
 arrayCopy[ --i ] = array[ i ] // doing stuff
);
Run Code Online (Sandbox Code Playgroud)

注意参数的设置:

  1. 与while循环相同,我只使用一个变量
  2. 需要检查索引是否大于0;
  3. 正如你所看到的,这种方法与每个人使用的正常循环不同,因为我在第3个参数内部做了东西,而且我也直接在数组内部减少了.

说,这证实了机器喜欢 -

写这篇文章我想把它缩短一点并删除一些无用的东西并用同样的风格写下这个:

for(
 var i = array.length ;
 i-- ;
 arrayCopy[ i ] = array[ i ] // doing stuff
);
Run Code Online (Sandbox Code Playgroud)

即使它更短,看起来再使用i一次会减慢一切.它比前一个for循环慢了1/5 while.

注:;以后的looo是非常重要的,而不{}

即使我刚刚告诉你jsperf不是测试脚本的最好方法..我在这里添加了这2个循环

http://jsperf.com/caching-array-length/40

这是关于javascript性能的另一个答案

/sf/answers/1494712271/

这个答案是为了展示编写javascript的高效方法.因此,如果您无法阅读,请询问并获得答案或阅读有关javascript的书籍http://www.ecma-international.org/ecma-262/5.1/

  • "机器更喜欢这个"听起来像一个洗衣粉广告的句子 (9认同)

prz*_*moc 11

http://jsperf.com/caching-array-length/60

我准备的最新版测试(通过重用旧版本)显示了一件事.

缓存长度不是那么重要,但它并没有害处.

上面链接的每个第一次测试(在新打开的选项卡上)都会在我的Debian Squeeze 64位(我的桌面硬件)中为Chrome,Opera和Firefox中的最后4个片段(图表中的第3,第5,第7和第10个)提供最佳结果).后续运行给出了完全不同的结果.

性能方面的结论很简单:

  • 转到for循环(转发)并使用!==而不是使用<.
  • 如果您以后不必重新使用该数组,那么while循环减少长度和破坏性shift()数组也很有效.

TL;博士

如今(2011.10)下面的模式看起来是最快的.

for (var i = 0, len = arr.length; i !== len; i++) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

请注意,缓存arr.length在这里并不重要,所以你可以测试i !== arr.length并且性能不会下降,但是你会得到更短的代码.


PS:我知道在片段中shift()它的结果可以用来代替访问第0个元素,但是我在某种程度上忽略了重用之前的修订版(它有错误的while循环),后来我不想丢失已经获得的结果.


val*_*aca 8

纯粹表现中的"最佳"?或性能可读性?

纯粹的性能"最好"是这个,它使用缓存和++前缀运算符(我的数据:http://jsperf.com/caching-array-length/189)

for (var i = 0, len = myArray.length; i < len; ++i) {
  // blah blah
}
Run Code Online (Sandbox Code Playgroud)

我认为无缓存for循环是执行时间和程序员阅读时间的最佳平衡.每个以C/C++/Java开头的程序员都不会浪费ms来阅读这篇文章

for(var i=0; i < arr.length; i++){
  // blah blah
}
Run Code Online (Sandbox Code Playgroud)

  • +1表示可读性.无论`len`的命名有多好,人们都必须在第一个循环中进行双重处理.第二个循环的意图是显而易见的. (2认同)

Shu*_*gar 7

**缓存循环内的数组长度,几秒钟的时间将被躲避.如果数组中有更多项目,那么取决于数组中的项目,与时间的Ms*有很大的不同

**

sArr; //Array[158];

for(var i = 0 ; i <sArr.length ; i++) {
 callArray(sArr[i]); //function call
}

***end: 6.875ms***
Run Code Online (Sandbox Code Playgroud)

**

**

sArr; //Array[158];
for(var i = 0,len = sArr.length ; i < len ; i++) {
  callArray(sArr[i]); //function call
}

***end: 1.354ms***
Run Code Online (Sandbox Code Playgroud)

**


Ser*_*gio 6

看起来是迄今为止最快的方式 ......

var el;
while (el = arr.shift()) {
  el *= 2;
}
Run Code Online (Sandbox Code Playgroud)

考虑到这将消耗阵列,吃掉它,不留任何东西......

  • `arr.shift();`而不是`arr.pop()`这样可以避免数组反转. (2认同)
  • 目前在 Chrome 53 和 Firefox 48 中,这是最慢的方法之一 - 检查 [perfjs.info/array-iteration](http://perfjs.info/#!/2DDD96CD-1F90-4792-9CE8-D6CA07CFC248) (2认同)