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

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

//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 l = []; // Will hold the array of Node's
for(var i = 0, ll = nl.length; i != ll; l.push(nl[i++]));

EDIT2:我找到了一个更快的方法

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

2021年更新:nodeList.forEach()现在是标准的并支持所有当前浏览器(大约95%的桌面和放大器;移动)。

所以你可以简单地做:

document.querySelectorAll('img').forEach(highlight);

其他情况下

如果你出于某种原因想要将它转换为一个数组,而不仅仅是迭代它——这是一个完全相关的用例——你可以从ES6开始使用[...destructuring]Array.from

let array1 = [...mySetOfElements];
// or
let array2 = Array.from(mySetOfElements);

这也适用于其他不是nodelist的类似数组的结构

  • 由例如document.getElementsByTagName返回的HTMLCollection
  • 具有length属性和索引元素的对象
  • 可迭代对象(例如MapSet)



过时的2010年答案

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

@kangax (IE 9预览)

Array.prototype.slice现在可以转换 某些宿主对象(例如NodeList的) 到数组,这是大多数 现代浏览器已经能够做到这一点

例子:

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

看看这个博客帖子在这里,它谈论了同样的事情。据我所知,多出来的时间可能是为了爬瞄准镜链。

结果将完全取决于浏览器,为了给出客观的判断,我们必须做一些性能测试,这里是一些结果,你可以运行它们在这里:

Chrome 6:

< img src = " https://chart.apis.google.com/chart?chtt=NodeList%20to%20Array%7COps/sec%20%28Chrome%206.0.453.1%20on%20Windows%20NT%29&本= 000000,十,十= bhg&冠心病= t: 3632年,3994年,3652年,冠心病= 0,3994,chxt = x& chxl = 0: | 0 | 4 k& chsp = 0, 1, chm = tArray.prototype.slice.call % 283.6 k % 29日,000000年,0,0,10 | tfor % 20循环% 284 k % 29, 000000 0, 1, 10 | treverse % 20 % 20循环% 283.7 k时% 29,000000 0,2,10,chbh = 15 0 5, chs = 250 x130 " / >

Firefox 3.6:

< img src = " https://chart.apis.google.com/chart?chtt=NodeList%20to%20Array%7COps/sec%20%28Firefox%203.6.3%20on%20Windows%20NT%29&本= 000000,十,十= bhg&冠心病= t: 1037年,1333年,1065年,冠心病= 0,1333,chxt = x& chxl = 0: | 0 | 1.3 k& chsp = 0, 1, chm = tArray.prototype.slice.call % 281 k % 29日,000000年,0,0,10 | tfor % 20循环% 281.3 k % 29, 000000 0, 1, 10 | treverse % 20 % 20循环% 281.1 k时% 29,000000 0,2,10,chbh = 15 0 5, chs = 250 x130 " / >

Firefox 4.0 b2:

Safari 5:

< img src = " https://chart.apis.google.com/chart?chtt=NodeList%20to%20Array%7COps/sec%20%28Safari%205.0%20on%20Windows%20NT%29&本= 000000,十,十= bhg&冠心病= t: 4881年,3418年,4281年,冠心病= 0,4881,chxt = x& chxl = 0: | 0 | 4.9 k& chsp = 0, 1, chm = tArray.prototype.slice.call % 284.9 k % 29日,000000年,0,0,10 | tfor % 20循环% 283.4 k % 29, 000000 0, 1, 10 | treverse % 20 % 20循环% 284.3 k时% 29,000000 0,2,10,chbh = 15 0 5, chs = 250 x130 " / >

IE9平台预览3:

一些优化:

  • 在一个变量中保存节点列表的长度
  • 在设置之前显式地设置新数组的长度。
  • 访问索引,而不是推动或不变。

代码(jsPerf):

var arr = [];
for (var i = 0, ref = arr.length = nl.length; i < ref; i++) {
arr[i] = nl[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]);

当我比较的时候

http://jsbin.com/oqeda/98/edit

*感谢@CMS的想法!

铬(类似于谷歌铬) Firefox < img src = " https://i.stack.imgur.com/4exJk.png " alt = "歌剧”> < / p >

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

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

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

这里有一个使用ES6扩展算子的新方法:

let arr = [...nl];

在ES6中,我们现在有了一个从NodeList创建Array的简单方法:Array.from()函数。

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

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

Safari 11.1.2 Firefox 61.0 Chrome 68.0.3440.75 Internet Explorer 11.15.16299.0

.

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

在ES6中,你可以使用:

  • < p > Array.from

    let array = Array.from(nodelist)

  • < p >传播算子

    let array = [...nodelist]

假设nodeList = document.querySelectorAll("div"),这是将nodelist转换为数组的简洁形式。

var nodeArray = [].slice.call(nodeList);

看我用它在这里

最简单的方法:

Array.from(document.querySelectorAll('.back-top'))

这里有一行,我不确定它是否安全,但它对我有用,它用一个数组覆盖了节点列表变量,因为我不再使用节点列表,因为我把它转换成一个数组。我发现这个解决方案更简洁,因为它只使用了一个变量。

this.openButtons = [...this.openButtons]