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

Most efficient way to convert an HTMLCollection to an Array

提问人:Tom 提问时间:10/22/2008 更新时间:3/4/2023 访问量:487786

问:

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

JavaScript Arrays 对象

评论

14赞 RobG 1/29/2015
“高效”是什么意思?如果性能最好,for 循环通常比 Array.prototype.slice 快。循环也适用于更广泛的浏览器(即所有浏览器),因此根据这些标准,它是“最有效的方式”。而且它的代码很少:所以那里没有太多的“骗局”:-)for (var a=[], i=collection.length; i;) a[--i] = collection[i];
0赞 Slashback 1/3/2016
@RobG 谢谢 - 如果可以的话,我会给你 +59k!;-)
1赞 RobG 1/5/2016
目前的浏览器性能来看,slice 在性能方面基本上赶上了循环,除了 Chrome。使用更多的元素和对循环的轻微优化,结果几乎相同,除了在 Chrome 中,循环速度要快得多。
0赞 NuclearPeon 7/12/2017
我创建了一个 jsperf 测试,它查看@harpo提到的两种方法以及性能的 jquery 测试。我发现jquery比两种javascript方法都略慢,并且js测试用例之间的最佳性能各不相同。Chrome 59.0.3071 / Mac OS X 10.12.5 更喜欢使用 和 Brave(基于 Chrome 59.0.3071)在多次运行的两个 javascript 测试之间几乎没有区别。查看 jsperf.com/htmlcollection-array-vs-jquery-childrenArray.prototype.slice.call
1赞 EscapeNetscape 2/13/2020
jsben.ch/h2IFA => 性能测试,了解最常见的执行此操作的方法

答:

1084赞 harpo 10/22/2008 #1
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 还提供了 spread 运算符,它在功能上等同于(但请注意,它支持映射函数作为第二个参数)。Array.fromArray.from

var arr = [...htmlCollection];

我已经确认上述两者都适用于.NodeList

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

评论

35赞 Oriol 4/25/2014
快捷方式也有效。[].slice.call(htmlCollection)
1赞 Erik Reppen 6/11/2014
@ChrisNielsen 是的,我被误导了。很抱歉传播它。我没有意识到我在这里也说过。删除了注释以避免混淆,但就上下文而言,我在某处阅读(或误读)了对 HTMLCollection 进行切片使其行为既像数组又像集合。完全不正确。
3赞 JussiR 2/8/2017
[].slice 快捷方式不等效,因为它还会创建未使用的空数组实例。不过,不确定编译器是否能够对其进行优化。
5赞 Frank Conijn - Support Ukraine 6/17/2018
Array.from,即 ,不受 IE11 支持。from
5赞 Yukulélé 11/13/2021
Typescript 不允许 spread 运算符,因为 htmlCollection 没有方法。[Symbol.iterator]()
7赞 Gareth Davis 12/9/2009 #2

对于跨浏览器实现,我建议您查看原型 .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;
}

它不使用可能是因为它不是在每个浏览器上都可用。恐怕性能非常糟糕,因为有一个回退是 javascript 循环。Array.prototype.sliceiterable

评论

2赞 Luc125 11/13/2011
OP 要求采用另一种方式,而不是“遍历所述集合的内容并手动将每个项目推入数组”,但这正是该函数大多数时候所做的。$A
1赞 Gareth Davis 11/14/2011
我认为我想表达的观点是没有一个很好的方法可以做到这一点,prototype .js 代码表明您可以寻找“toArray”方法,但失败该迭代是最安全的路线
1赞 RobG 1/4/2016
这将在稀疏数组中创建新的、未定义的成员。在分配之前应该有一个 hasOwnProperty 测试。
3赞 Gustavo 5/17/2013 #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;
}

如果浏览器支持最好的方式,好的,否则将使用跨浏览器。

评论

0赞 Patrick 4/27/2015
总的来说,我不认为 try/catch 是管理控制流的有效方法。您可以先检查该函数是否存在,然后以更便宜的方式运行一个或另一个。
2赞 RobG 1/4/2016
与 Gareth Davis 的答案一样,这会在稀疏数组中创建新的、未定义的成员,因此变为 .[,,][undefined, undefined]
0赞 Gustavo 3/15/2016
我还没有遇到这种麻烦。它将一个 3 元素集合接缝成一个包含 2 个元素的数组。至于 empty 变得未定义,这是 JavaScript 的一点限制,我想你期待的是 null 而不是 undefined,对吧?
26赞 Codesmith 3/27/2014 #4

我看到了一种更简洁的方法来获取方法,它同样有效。将对象转换为对象的过程如下所示:Array.prototypeHTMLCollectionArray

[].slice.call( yourHTMLCollectionObject );

而且,正如评论中提到的,对于IE7及更早版本等旧浏览器,您只需使用兼容性功能,例如:

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

    return a
}

我知道这是一个老问题,但我觉得公认的答案有点不完整;所以我想我会把它扔在那里 FWIW。

136赞 mido 5/5/2016 #5

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

let arry = [...htmlCollection] 

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

let arry = Array.from(htmlCollection)

评论

11赞 Chris_F 6/13/2016
此外,ES6 还增加了Array.from()
4赞 Marcel M. 3/16/2017
注意第一个,在使用 babel 进行转译时有一个微妙的错误,其中 [...htmlCollection] 将返回一个数组,其中 htmlCollection 是唯一的元素。
3赞 Bobby 7/4/2017
数组扩展运算符不适用于 htmlCollection。它仅适用于 NodeList。
1赞 Frank Conijn - Support Ukraine 6/17/2018
Array.from,即 ,不受 IE11 支持。from
0赞 RedSparr0w 6/27/2018
基准看起来点差运算符在这 2 个中更快。
6赞 Nicholas 8/13/2016 #6

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

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

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

评论

0赞 Shahar Shokrani 7/11/2018
尝试var args = (htmlCollection.length === 1 ? [htmlCollection[0]] : Array.apply(null, htmlCollection));
5赞 Shahar Shokrani 7/11/2018 #7

为了有效地将 array-like 转换为 array,我们可以利用 jQuerymakeArray

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 属性。但这就是相似性停止的地方。 类数组对象没有任何 Array 的功能,并且 for-in 循环甚至不起作用!

5赞 Roman Karagodin 2/1/2021 #8

我认为在实例上调用 Array.prototype 函数比将集合转换为数组(例如,或)要好得多,因为在后一种情况下,集合被不必要地隐式迭代并创建了一个新的数组对象,这会占用额外的资源。 迭代函数可以安全地调用具有从 开始的连续数字键的对象,并且具有此类键数量的有效数字值的属性(包括 ,例如,和 的实例),因此这是一种可靠的方法。此外,如果经常需要此类操作,则可以使用空数组来快速访问函数。一个可运行的示例:HTMLCollection[...collection]Array.from(collection)Array.prototype[0]lengthHTMLCollectionFileList[]Array.prototype

alert(
    Array.prototype.reduce.call(
        document.querySelector('ol').children,
        (acc, { textContent }, i) => `${acc}${i + 1}) ${textContent}` + `\n`,
        '',
    ),
);
<ol>
    <li>foo</li>
    <li>bar</li>
    <li>bat</li>
    <li>baz</li>
</ol>

3赞 Avi 1/4/2022 #9

有时,即使你以正确的方式编写了代码,但它仍然无法正常工作。

var allbuttons = document.getElementsByTagName("button");
console.log(allbuttons);

var copyAllButtons = [];
for (let i = 0; i < allbuttons.length; i++) {
  copyAllButtons.push(allbuttons[i]);
}
console.log(copyAllButtons);

你得到空数组。 喜欢这个

HTMLCollection []
[]

Console_javascript

为了解决这个问题,您必须在html文件中的body标签后添加javascript文件的链接。

<script src="./script.js"></script>

正如你在下面看到的,html_file

最终输出

HTMLCollection(6) [button.btn.btn-dark.click-me, button.btn.btn-dark.reset, button#b, button#b, button#b, button#b, b: button#b]
(6) [button.btn.btn-dark.click-me, button.btn.btn-dark.reset, button#b, button#b, button#b, button#b]
0赞 hanan 3/4/2023 #10

我不确定效率,但从纯粹的美学角度来看,我觉得这很令人愉快。

HTMLCollection.prototype.toArray = function() { return Array.from(this); }

现在你可以这样使用它了。

document.getElementsByClassName('tax-status').toArray().forEach(console.log);