将HTMLCollection转换为数组的最有效方法


391

除了遍历所述集合的内容并将每个项目手动推入数组之外,是否有更有效的方法将HTMLCollection转换为数组?


10
什么是“高效”?如果性能最佳,则for循环通常比Array.prototype.slice快。循环还可以在各种各样的浏览器(即所有浏览器)中使用,因此按照这些标准,它 “最有效的方式”。而且这是很少的代码:for (var a=[], i=collection.length; i;) a[--i] = collection[i];那里没有太多的“骗子” :-)
RobG 2015年

@RobG谢谢-如果可以的话,我给您+ 59k!;-)
Slashback

1
查看当前浏览器的性能,除Chrome之外,slice性能方面几乎都陷入了循环。使用大量元素并略微优化循环,结果几乎完全相同,但在Chrome中,循环快得多。
RobG '16

我创建了一个jsperf测试,该测试查看@harpo提到的两种方法以及性能的jquery测试。我发现jquery比javascript方法都慢一点,并且js测试用例之间的最高性能有所不同。Chrome 59.0.3071 / Mac OS X 10.12.5更喜欢使用,Array.prototype.slice.call并且Brave(基于Chrome 59.0.3071)在多次运行之间在两种JavaScript测试之间几乎没有区别。参见jsperf.com/htmlcollection-array-vs-jquery-children
NuclearPeon '17

jsben.ch/h2IFA =>性能测试最常用的方法
EscapeNetscape

Answers:


696
var arr = Array.prototype.slice.call( htmlCollection )

使用“本地”代码将具有相同的效果。

编辑

由于这有很多观点,请注意(按@oriol的评论),以下更简洁的表达实际上等效:

var arr = [].slice.call(htmlCollection);

但是,请注意@JussiR的注释,它与“冗长的”形式不同,它确实在该过程中创建了一个空的,未使用的,实际上是不可用的数组实例。编译器对此所做的工作超出了程序员的范围。

编辑

从ECMAScript 2015(ES 6)开始,还有Array.from

var arr = Array.from(htmlCollection);

编辑

ECMAScript 2015还提供了散布运算符,其功能等效于Array.from(尽管请注意,它Array.from支持将映射功能作为第二个参数)。

var arr = [...htmlCollection];

我已经确认上述两项工作都在上进行NodeList

上述方法的性能比较:http : //jsben.ch/h2IFA


7
这在IE6中失败。
Heath Borders

29
快捷方式[].slice.call(htmlCollection)也有效。
Oriol 2014年

1
@ChrisNielsen是的,我对此误会了。对不起,到处传播。我没意识到我也在这里说过。为了避免混淆,删除了注释,但是在上下文中,我已经阅读(或错误阅读)了切片HTMLCollection的地方,使其表现得既像数组又像集合。完全不正确。
Erik Reppen 2014年

3
[] .slice快捷方式并不等效,因为它还会创建未使用的空数组实例。不过,不确定编译器是否能够对其进行优化。
JussiR

3
Array.from,即fromIE11不支持。
Frank Conijn

86

不知道这是否是最有效的,但是简洁的ES6语法可能是:

let arry = [...htmlCollection] 

编辑:另一个,来自Chris_F评论:

let arry = Array.from(htmlCollection)

9
此外,ES6添加了Array.from()
Chris_F,2013年

4
注意第一个,使用babel进行编译时有一个微妙的错误,其中[... htmlCollection]将返回一个带有htmlCollection的数组,因为它是唯一的元素。
Marcel M.

3
数组传播运算符不适用于htmlCollection。它仅适用于NodeList。
Bobby

1
Array.from,即fromIE11不支持。
Frank Conijn

基准看起来像传播算子从这2个中更快
。– RedSparr0w

20

我看到了一种更简洁的方法,该Array.prototype方法通常也可以正常使用。将HTMLCollection对象转换为Array对象的示例如下所示:

[] .slice.call(yourHTMLCollectionObject);

而且,如评论中所述,对于IE7和更早版本的旧浏览器,您只需使用兼容功能,例如:

function toArray(x) {
    for(var i = 0, a = []; i < x.length; i++)
        a.push(x[i]);

    return a
}

我知道这是一个古老的问题,但是我觉得可以接受的答案有点不完整。所以我以为我会把它扔出去。


6

对于跨浏览器的实现,我建议您看一下prototype.js $A函数

从1.6.1复制

function $A(iterable) {
  if (!iterable) return [];
  if ('toArray' in Object(iterable)) return iterable.toArray();
  var length = iterable.length || 0, results = new Array(length);
  while (length--) results[length] = iterable[length];
  return results;
}

Array.prototype.slice之所以未使用,可能是因为它并非在所有浏览器中都可用。恐怕性能会很差,因为回退是上的javascript循环iterable


2
OP要求采用另一种方式,而不是“遍历所述集合的内容并手动将每个项目推入数组中”,但这恰恰是该$A函数在大多数情况下的作用。
Luc125

1
我认为我要说的是,没有一种很好的方法可以做到这一点,prototype.js代码显示,您可以寻找“ toArray”方法,但是如果迭代失败,则是最安全的方法
Gareth Davis

1
这将在稀疏数组中创建新的未定义成员。分配之前应该有一个hasOwnProperty测试。
RobG '16

3

这是我的个人解决方案,基于此处的信息(此线程):

var Divs = new Array();    
var Elemns = document.getElementsByClassName("divisao");
    try {
        Divs = Elemns.prototype.slice.call(Elemns);
    } catch(e) {
        Divs = $A(Elemns);
    }

Gareth Davis在他的帖子中描述了$ A的位置:

function $A(iterable) {
  if (!iterable) return [];
  if ('toArray' in Object(iterable)) return iterable.toArray();
  var length = iterable.length || 0, results = new Array(length);
  while (length--) results[length] = iterable[length];
  return results;
}

如果浏览器支持最佳方式,可以,否则将使用跨浏览器。


通常,我不希望try / catch是管理控制流的有效方法。您可以先检查该功能是否存在,然后再便宜一点地运行其中一个。
Patrick

2
与Gareth Davis的答案一样,这会在稀疏数组中创建新的未定义成员,因此[,,]变为[undefined, undefined]
RobG '16

我还没有遇到这种麻烦。它接缝了3个元素的集合,结果是一个包含2个元素的数组。至于empty变为undefined,这是JavaScript的一些限制,我想您期望的是null而不是undefined,对吗?
古斯塔沃

3

这适用于所有浏览器,包括早期的IE版本。

var arr = [];
[].push.apply(arr, htmlCollection);

由于jsperf目前仍处于关闭状态,因此这里有一个jsfiddle比较了不同方法的性能。https://jsfiddle.net/qw9qf48j/


试试var args = (htmlCollection.length === 1 ? [htmlCollection[0]] : Array.apply(null, htmlCollection));
Shahar Shokrani,

3

为了高效地将类似数组的数组转换为数组,我们可以使用jQuery makeArray

makeArray:将类似数组的对象转换为真正的JavaScript数组。

用法:

var domArray = jQuery.makeArray(htmlCollection);

一点额外:

如果您不想保留对数组对象的引用(大多数时候HTMLCollections是动态更改的,那么最好将它们复制到另一个数组中,此示例将密切关注性能:

var domDataLength = domData.length //Better performance, no need to calculate every iteration the domArray length
var resultArray = new Array(domDataLength) // Since we know the length its improves the performance to declare the result array from the beginning.

for (var i = 0 ; i < domDataLength ; i++) {
    resultArray[i] = domArray[i]; //Since we already declared the resultArray we can not make use of the more expensive push method.
}

什么是数组状?

的HTMLCollection是一个"array-like"对象,该阵列状的对象类似于数组的对象,但缺少了许多功能上的定义:

类数组对象看起来像数组。它们具有各种编号的元素和length属性。但这就是相似性停止的地方。类似数组的对象没有数组的任何功能,for-in循环甚至无法工作!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.