将JavaScript节点列表转换为数组的最快方法?

将JavaScript节点列表转换为数组的最快方法?,javascript,arrays,nodelist,Javascript,Arrays,Nodelist,之前在这里回答的问题说这是最快的方式: //nl is a NodeList var arr = Array.prototype.slice.call(nl); 在对我的浏览器进行基准测试时,我发现它的速度比这个慢3倍多: var arr = []; for(var i = 0, n; n = nl[i]; ++i) arr.push(n); 它们都产生相同的输出,但我发现很难相信我的第二个版本是最快的方式,特别是因为人们在这里说了其他方式 这是我的浏览器(Chromium 6)中的一个怪癖

之前在这里回答的问题说这是最快的方式:

//nl is a NodeList
var arr = Array.prototype.slice.call(nl);
在对我的浏览器进行基准测试时,我发现它的速度比这个慢3倍多:

var arr = [];
for(var i = 0, n; n = nl[i]; ++i) arr.push(n);
它们都产生相同的输出,但我发现很难相信我的第二个版本是最快的方式,特别是因为人们在这里说了其他方式

这是我的浏览器(Chromium 6)中的一个怪癖吗?还是有更快的办法

编辑:对于任何关心我的人,我决定做以下几点(这似乎是我测试的所有浏览器中速度最快的):

我找到了一个更快的方法

// nl is the nodelist
var arr = [];
for(var i = nl.length; i--; arr.unshift(nl[i]));

在某些浏览器中,第二种方法的速度更快,但主要的一点是,您必须使用它,因为第一种方法不是跨浏览器的。即使时代在改变

IE 9预览版)

Array.prototype.slice现在可以转换 某些主机对象(例如节点列表) 到数组-大多数 现代浏览器已经能够做到这一点 好一阵子了

例如:

Array.prototype.slice.call(document.childNodes);

看看这篇关于同样事情的博文。据我所知,额外的时间可能与在范围链上行走有关。

结果将完全取决于浏览器,为了给出客观的结论,我们必须进行一些性能测试,以下是一些结果,您可以运行它们:

铬6:

Firefox 3.6:

Firefox 4.0b2:

狩猎5:

IE9平台预览3:

一些优化:

  • 将节点列表的长度保存在变量中
  • 在设置之前显式设置新数组的长度
  • 访问索引,而不是推动或取消移动
代码():

var arr=[];
对于(变量i=0,ref=arr.length=nl.length;i
更快、更短:

// nl is the nodelist
var a=[], l=nl.length>>>0;
for( ; l--; a[l]=nl[l] );

最快速和跨浏览器的是

for(var i=-1,l=nl.length;++i!==l;arr[i]=nl[i]);
正如我在

*谢谢@CMS的创意


这是我在JS中使用的函数:

function toArray(nl) {
    for(var a=[], l=nl.length; l--; a[l]=nl[l]);
    return a;
}

现在您可以使用document.querySelectorAll('div').forEach(function()…)

以下是一种新的很酷的方法:


使用ES6,我们现在有了一种从节点列表创建数组的简单方法:函数

// nl is a NodeList
let myArray = Array.from(nl)

以下是截至发布日期更新的图表(“未知平台”图表为Internet Explorer 11.15.16299.0):


从这些结果来看,preallocate 1方法似乎是最安全的跨浏览器赌注。

在ES6中,您可以使用:

  • Array.from

    let array=array.from(节点列表)

  • 扩展运算符

    let数组=[…节点列表]


假设
节点列表=document.queryselectoral(“div”)
,这是将
节点列表
转换为数组的简明形式

var noderray=[].slice.call(nodeList);
看我用它。

就这么做吧

vat arr=[…节点列表]

有趣。我现在刚刚做了一些类似的测试,Firefox 3.6.3无论采用哪种方式,速度都没有提高,而Opera 10.6的速度提高了20%,Chrome 6的速度提高了230%(!)采用手动迭代推送方式。@jairajs89非常奇怪。似乎
Array.prototype.slice
依赖于浏览器。我想知道每个浏览器都在使用什么算法。但是它们不能在IE中的节点列表上使用(我知道这很糟糕,但是嘿,看看我的更新),因为节点列表不是语言的一部分,它们是DOM API的一部分,这是众所周知的有缺陷/不可预测的,特别是在IEArray中。prototype.slice不是跨浏览器的,如果你考虑IE8的话。是的,这就是我的答案的基本内容:)虽然2010年比今天(2015年)更相关。这可以缩短为
[].slice.call(document.childNodes)
:)哦,快乐的一天我想知道反向for循环如何与这些相反<代码>用于(变量i=o.length;i--;)
。。。这些测试中的“for循环”是否在每次迭代时重新评估了length属性?
arr[arr.length]=nl[i]
可能比arr.push(nl[i])快因为它避免了函数调用。此jsPerf页面跟踪此页面上的所有答案:请注意,IE8上的“EDIT2:我找到了一种更快的方法”速度慢92%。因为您已经知道有多少节点:
var I=nl.length,arr=new Array(I);对于(;i--;arr[i]=nl[i])@Luc125这取决于浏览器,因为推送实现可能会得到优化,所以我考虑使用chrome,因为v8很适合这种东西。为什么要使用
>>0
?为什么不把赋值放在for循环的第一部分呢?还有,这是个错误。当
l
0
时,循环将结束,因此不会复制
0
第th个元素(请记住索引
0
处有一个元素)我喜欢这个答案,但是。。。任何想知道的人:这里可能不需要
,但用于保证节点列表的长度符合阵列规范;它确保它是一个无符号32位整数。如果您喜欢不可读的代码,请在这里查看,并结合@CamiloMartin的建议使用此方法!作为对@CamiloMartin的回复,由于“变量提升”,将
var
放在
for
循环的
第一部分是有风险的。
var
的声明将被“提升”到函数的顶部,即使
var
行出现在较低的位置,这可能会导致代码序列中不明显的副作用。例如,在For循环之前出现的同一函数中的某些代码可能依赖于
a
l
未声明。因此,为了获得更高的可预测性,请在函数顶部声明VAR(或者如果在ES6上,则使用
const
let
,它们不会提升)。链接似乎是错误的,应该是91,而不是89以包含您提到的测试。和98 se
function toArray(nl) {
    for(var a=[], l=nl.length; l--; a[l]=nl[l]);
    return a;
}
NodeList.prototype.forEach = Array.prototype.forEach;
let arr = [...nl];
// nl is a NodeList
let myArray = Array.from(nl)