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

我从书中学到,你应该这样写for循环:

for(var i=0, len=arr.length; i < len; i++){
// blah blah
}

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

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

for(var i=0; i < arr.length; i++){
// blah blah
}

我只是想知道在实践中哪种方法是最好的?

310729 次浏览

我总是用第一种体裁写作。

即使编译器足够智能,可以为数组优化它,但如果我们在这里使用DOMNodeList或一些计算长度的复杂对象,它仍然是智能的?

我知道关于数组的问题是什么,但我认为用一种风格编写所有循环是一个很好的实践。

循环javascript数组的最快方法是:

var len = arr.length;
while (len--) {
// blah blah
}

完整的比较参见这篇文章

如果顺序不重要,我更喜欢这种风格:

for(var i = array.length; i--; )

它缓存了长度,写起来要短得多。但是它会以相反的顺序遍历数组。

在使用大多数现代浏览器执行此测试后: https://jsben.ch/wY5fo < / p >

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

具有长度缓存的标准for循环

    var i = 0, len = myArray.length;
while (i < len) {
// your code
i++
}

我想说,这绝对是我为JavaScript引擎开发者喝彩的地方。运行时应该针对清晰,而不是聪明进行优化。

http://jsperf.com/caching-array-length/60 < a href = " http://jsperf.com/caching-array-length/60 " > < / >

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

缓存长度并不是那么重要,但也没有坏处。

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

性能方面的结论很简单:

  • 使用for循环(向前)并使用!==而不是<进行测试。
  • 如果以后不需要重用数组,则while循环对长度递减和破坏shift()-ing数组也是有效的。

博士tl;

现在(2011.10)下面的模式看起来是最快的。

for (var i = 0, len = arr.length; i !== len; i++) {
...
}

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


PS:我知道在shift()的代码片段中,它的结果可以用来代替访问第0个元素,但我在重用以前的修订(有错误的while循环)后忽视了这一点,后来我不想失去已经获得的结果。

我所知道的最优雅的解决方案是使用地图。

var arr = [1,2,3];
arr.map(function(input){console.log(input);});

“最好”是指纯粹的表演吗?或性能可读性?

纯性能“最佳”是这个,它使用缓存和++前缀操作符(我的数据:http://jsperf.com/caching-array-length/189)

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

我认为无缓存的for循环是执行时间和程序员读取时间的最佳平衡。每一个从C/ c++ /Java开始的程序员都不会浪费时间去阅读这本书

for(var i=0; i < arr.length; i++){
// blah blah
}

2014 While回来了

要有逻辑性。

看看这个

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


//do stuff


}
  1. 需要创建至少2个变量(index,length)
  2. 需要检查索引是否小于长度
  3. 需要增加索引
  4. for循环有3个参数

现在告诉我为什么这个要比

var length = array.length;


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


//do stuff


}
  1. 一个变量
  2. 没有检查
  3. 指数下降(机器更喜欢这样)
  4. while只有一个参数
当Chrome 28显示for循环比while快时,我完全困惑了。 这一定是某种

“嗯,每个人都在使用for循环,让我们把重点放在什么时候 开发铬。" < / em > < / p >

但是现在,在2014年,while循环又回到了chrome上。它快了2倍,在其他/旧浏览器上它总是更快。

最近我做了一些新的测试。现在在现实环境中,这些短代码毫无价值,jsperf实际上不能正确执行while循环,因为它需要重新创建数组。长度也需要时间。

你无法在jsperf上获得while循环的实际速度。

你需要创建你自己的自定义函数,并用window.performance.now()检查

是的……while循环不可能简单地更快。

真正的问题实际上是dom操作/渲染时间

例如,我有一个画布场景,我需要计算坐标和碰撞…这是在10-200微秒(不是毫秒)之间完成的。它实际上需要不同的毫秒来渲染所有内容。和DOM中一样。

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

for(
var i = array.length ;
i > 0 ;
arrayCopy[ --i ] = array[ i ] // doing stuff
);

注意参数的设置:

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

说到这里,这证实了机器喜欢

我想把它写得短一点,去掉一些无用的东西,用同样的风格写了这篇文章:

for(
var i = array.length ;
i-- ;
arrayCopy[ i ] = array[ i ] // doing stuff
);
即使它更短,它看起来像使用i一次减慢一切。 它比前面的for循环和while循环慢1/5

在没有{}的for looo之后,;非常重要

即使我只是告诉你,jsperf不是测试脚本的最佳方式。我在这里加了两个循环

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

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

https://stackoverflow.com/a/21353032/2450730 < a href = " https://stackoverflow.com/a/21353032/2450730 " > < / >

这个答案是为了展示编写javascript的性能方法。所以如果你看不懂,问一下,你会得到一个答案,或者读一本关于javascript的书http://www.ecma-international.org/ecma-262/5.1/

var arr = []; // The array
var i = 0;
while (i < arr.length) {
// Do something with arr[i]
i++;
}

i++比++ I,——I和I——快

此外,您可以保存最后一次需要访问i时执行arr[i++]的最后一行(但这可能很难调试)。

你可以在这里测试它(使用其他循环测试):http://jsperf.com/for-vs-whilepop/5

我已经尝试了一些其他方法来迭代一个巨大的数组,并发现将数组长度减半,然后在一个循环中迭代这两部分更快。这种性能差异可以在处理巨大的数组时看到。

var firstHalfLen =0;
var secondHalfLen = 0;
var count2=0;
var searchterm = "face";
var halfLen = arrayLength/2;
if(arrayLength%2==halfLen)
{
firstHalfLen = Math.ceil(halfLen);
secondHalfLen=Math.floor(halfLen);
}
else
{
firstHalfLen=halfLen;
secondHalfLen=halfLen;
}
for(var firstHalfCOunter=0,secondHalfCounter = arrayLength-secondHalfLen;
firstHalfCOunter < firstHalfLen;
firstHalfCOunter++)
{
if(mainArray[firstHalfCOunter].search(new RegExp(searchterm, "i"))> -1)
{
count2+=1;
}
if(secondHalfCounter < arrayLength)
{
if(mainArray[secondHalfCounter].search(new RegExp(searchterm, "i"))> -1)
{
count2+=1;
}
secondHalfCounter++;
}
}

缓存长度for循环与上面的方法之间的一些性能比较(使用timer.js)。

< a href = " http://goo。gl / 5 azpqz rel =“nofollow”> <强> http://jsfiddle.net/tejzpr/bbLgzxgo/ < /强> < / >

**缓存数组的长度在循环内,一些秒的时间将被规避。取决于数组中的项,如果数组中有更多项,则时间毫秒有很大差异*

**

sArr; //Array[158];


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


***end: 6.875ms***

**

**

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


***end: 1.354ms***

**

另一个jsperf.com测试:http://jsperf.com/while-reverse-vs-for-cached-length

反向while循环似乎是最快的。唯一的问题是while(——i)将停止于0。我怎么能访问数组[0]在我的循环那么?

这个看起来是最快的方法到目前为止…

var el;
while (el = arr.shift()) {
el *= 2;
}

考虑到这将消耗数组,吃掉它,不留下任何东西……

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

  • 最快的循环是for循环,有缓存长度和没有缓存长度都能提供非常相似的性能。(具有缓存长度的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
    }
    
  • If your app doesn't really iterate through lots of items or you just need to do small iterations here and there, using the standard forEach callback or any similar function from your JS library of choice might be more understandable and less prone to errors, since index variable scope is closed and you don't need to use brackets, accessing the array value directly:

    arr.forEach(function(value, index) {
    // Do stuff with value or index
    });
    
  • If you really need to scratch a few milliseconds while iterating over billions of rows and the length of your array doesn't change through the process, you might consider caching the length in your for loop. Although I think this is really not necessary nowadays:

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

这是2017年。

我做了一些测试

https://jsperf.com/fastest-way-to-iterate-through-an-array/

看起来while方法在Chrome上是最快的。

看起来左边递减(--i)比Firefox上的其他递减(++ii--i++)快得多。

这种方法平均来说是最快的。但是它以相反的顺序迭代数组。

let i = array.length;
while (--i >= 0) {
doSomething(array[i]);
}

如果前向顺序很重要,可以使用这种方法。

let ii = array.length;
let i = 0;
while (i < ii) {
doSomething(array[i]);
++i;
}

截至2017年9月,这些jsperf测试在Chrome 60上显示了以下模式:

function foo(x) {
x;
};
arr.forEach(foo);

有人能繁殖吗?

基本的while循环通常是最快的。Jsperf.com是测试这类概念的一个很好的沙盒。

https://jsperf.com/fastest-array-loops-in-javascript/24

现在是2018年,所以更新一下会很好……

和我真的必须不同意公认的答案。 它支持不同的浏览器。一些做forEach更快,一些做for-loop,还有一些做while 下面是所有方法http://jsben.ch/mW36e

的基准测试
arr.forEach( a => {
// ...
}

因为你可以看到很多for循环,比如for(a = 0; ... ),那么值得一提的是,如果没有'var'变量将被全局定义,这可能会极大地影响速度,所以它会变慢。

Duff的设备在opera上运行得更快,但在firefox上则不然
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 len = arr.length;
var i, n = len % 8 - 1;


if (n > 0) {
do {
var b = arr[len-n] + 1;
} while (--n); // n must be greater than 0 here
}
n = (len * 0.125) ^ 0;
if (n > 0) {
do {
i = --n <<3;
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;
}
while (n); // n must be greater than 0 here also
}
}]];
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;
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; } }
<div id="container"> </div>

试试这个:

var myarray =[],
i = myarray.lenght;
while(i--){
// do somthing
}

截至2019年,WebWorker已经更加流行,对于大型数据集,我们可以使用WebWorker通过充分利用多核处理器来更快地处理。

我们还有Parallel.js,这使得WebWorker更容易用于数据处理。

最快的方法是传统的for循环。这里有一个更全面的性能比较。

https://gists.cwidanage.com/2019/11/how-to-iterate-over-javascript-arrays.html

如果你想要一个更快的循环,在循环外定义变量并使用下面的语法

  const iMax = lengthOftheLoop;
var i = 0;
for (; i < iMax; i++) {
console.log("loop"+i);
}

参考:https://medium.com/kbdev/voyage-to-the-most-efficient-loop-in-nodejs-and-a-bit-js-5961d4524c2e

虽然这是一个非常古老的问题,但也是一个非常有趣的问题,

请原谅我稍微改变一下这个问题,但我会在最后回答这个问题。

这个问题让我问自己,在js中是否有更好的循环方法:

所以我做了一些测试,以下是我发现的: enter image description here < / p >

对于1000_000记录:最好是forEach。

对于100条记录:这根本不重要。


回到你刚才的问题:

我创建的例子和问题不完全一样。但我发现了一些有趣的事情:

首先,就像你说的,arr。长度每次都会计算,如果它在比较语句I <加勒比海盗。长度……

注意:下面的arrLength变量不超过1000_000条记录的数量。

enter image description here

例如:这不会工作 enter image description here < / p >

但是这个会

enter image description here

这需要0.036秒。与数字不变的情况相比,这是非常大的……


总之,

最好使用FOREACH

在你的情况下:i<arr。长度需要更多的时间(通常在1.3左右)

see the tests: 查看测试 < / p >

我在这里有一个测试。检查这个! https://gist.github.com/DungGramer/7efdfefecaa1b8f5d6510202524dc751 < / p >

我的结果:

  • pop最快forEach有利于可读性和速度
  • do..whilefor
  • 如果没有条件,则循环更快(如if)
  • --i最快

现在是2022年底

有时根本没有用数组长度计算:

如果你有一个索引为var myArray = [1,2,3,4,25,999999999999]的数组,则使用任何索引增加1的数组解决方案(包括forEach)都是非常缓慢的

对我来说,最好的解决方案是:

for(let _i in myArray ) {
if(myArray[_i]) {
(function(s) {
///
})(myArray[_i])
}
}
Benchmarking [10000000] element array...
The fastest [for ++] took [76762166ns]
┌─────────┬───────────────────────┬────────────┬──────────┐
│ (index) │         type          │  time[ns]  │ baseline │
├─────────┼───────────────────────┼────────────┼──────────┤
│    0    │       'for ++'        │  76762166  │    1     │
│    1    │       'for of'        │  82407583  │   1.07   │
│    2    │   '--while forward'   │  83723083  │   1.09   │
│    3    │ 'do while forward --' │  83942958  │   1.09   │
│    4    │ '--do while forward'  │  84225584  │   1.1    │
│    5    │  'while forward --'   │  85156957  │   1.11   │
│    6    │    '--while >= 0'     │  89745916  │   1.17   │
│    7    │     '++ do while'     │  90306542  │   1.18   │
│    8    │     'for !== ++'      │  90319083  │   1.18   │
│    9    │       '-- for'        │  90360167  │   1.18   │
│   10    │   'for i length --'   │  90558833  │   1.18   │
│   11    │       '++ for'        │  90616125  │   1.18   │
│   12    │     'do while ++'     │  90657541  │   1.18   │
│   13    │   '--for i length'    │  90757708  │   1.18   │
│   14    │       'for --'        │  90799875  │   1.18   │
│   15    │      '++ while'       │  92697417  │   1.21   │
│   16    │     '++ for !=='      │  94488209  │   1.23   │
│   17    │         'pop'         │ 108399917  │   1.41   │
│   18    │      'while ++'       │ 109276500  │   1.42   │
│   19    │    'forEach call'     │ 147140124  │   1.92   │
│   20    │       'forEach'       │ 148886207  │   1.94   │
│   21    │         'map'         │ 207100583  │   2.7    │
│   22    │     'Array from'      │ 353166207  │   4.6    │
│   23    │       'flatMap'       │ 1213152082 │   15.8   │
│   24    │   'Object.keys map'   │ 1294475333 │  16.86   │
│   25    │       'for in'        │ 1338988749 │  17.44   │
└─────────┴───────────────────────┴────────────┴──────────┘

在Macbook Air M1 2020上测试。NodeJS 18。

对于包含10_000_000元素的数组,循环标准胜出。对于其他情况,请参阅我的要点:https://gist.github.com/episage/076ded007d0583f6a275f93a8c9c8047#file-result-txt

大声喊出@DungGramer。我修复了bug并提高了他的基准测试。