ChatGPT解决这个技术问题 Extra ChatGPT

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

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

“高效”是什么意思?如果性能最好,for 循环通常比 Array.prototype.slice 更快。循环也适用于更广泛的浏览器(即所有浏览器),因此根据这些标准,它“最有效的方式”。而且它的代码很少:for (var a=[], i=collection.length; i;) a[--i] = collection[i]; 所以那里没有太多的“骗局”:-)
@RobG 谢谢 - 如果可以的话,我会给你 +59k! ;-)
查看 current browser performanceslice 在性能方面几乎赶上了循环,Chrome 除外。使用更多的元素和对循环的轻微优化,results are almost identical,除了在 Chrome 中循环速度非常快的情况。
我创建了一个 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
jsben.ch/h2IFA =>执行此操作的最常见方法的性能测试

E
EscapeNetscape
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 operator,它在功能上等同于 Array.from(尽管请注意 Array.from 支持映射函数作为第二个参数)。

var arr = [...htmlCollection];

我已确认上述两项都适用于 NodeList

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


快捷方式 [].slice.call(htmlCollection) 也有效。
@ChrisNielsen 是的,我被误导了。很抱歉传播了这个。我没有意识到我在这里也说过。删除了评论以避免混淆,但对于上下文,我在某处阅读(或误读)切片 HTMLCollection 使其表现得既像数组又像集合。完全不正确。
[].slice 快捷方式不等效,因为它还会创建未使用的空数组实例。不过,不确定编译器是否能够优化它。
IE11 不支持 Array.from,即 from
Typescript 不允许展开运算符,因为 htmlCollection 没有 [Symbol.iterator]() 方法。
m
mido

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

let arry = [...htmlCollection] 

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

let arry = Array.from(htmlCollection)

此外,ES6 添加了 Array.from()
注意第一个,当使用 babel 进行编译时有一个微妙的错误,其中 [...htmlCollection] 将返回一个带有 htmlCollection 的数组,因为它是唯一的元素。
数组扩展运算符不适用于 htmlCollection。它仅适用于 NodeList。
IE11 不支持 Array.from,即 from
Benchmark 看起来展开运算符在这 2 个中更快。
H
Henk van Boeijen

我看到了一种更简洁的方法来获取 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
}

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


G
Gareth Davis

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

copyed from 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 可能是因为它并非在每个浏览器上都可用。恐怕性能很差,因为回退是 iterable 上的 javascript 循环。


OP 要求另一种方式,而不是“遍历所述集合的内容并手动将每个项目推入数组”,但这正是 $A 函数大部分时间所做的事情。
我认为我试图说明的一点是没有一个很好的方法来做到这一点,prototype.js 代码显示您可以寻找一个“toArray”方法,但迭代失败是最安全的路线
这将在稀疏数组中创建新的未定义成员。在分配之前应该有一个 hasOwnProperty 测试。
N
Nicholas

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

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

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


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

为了以有效的方式将类数组转换为数组,我们可以使用 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" 对象,array-like 对象类似于数组的对象,但缺少很多功能定义:

类数组对象看起来像数组。它们有各种编号的元素和一个长度属性。但这就是相似之处停止的地方。类数组对象没有 Array 的任何功能,for-in 循环甚至不起作用!


G
Gustavo

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

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;
}

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


一般来说,我不希望 try/catch 成为管理控制流的有效方式。您可以先检查该功能是否存在,然后运行其中一个或另一个便宜一点。
与 Gareth Davis 的回答一样,这会在稀疏数组中创建新的未定义成员,因此 [,,] 变为 [undefined, undefined]
我还没有遇到这种麻烦。它将 3 个元素的集合拼接成一个包含 2 个元素的数组。至于empty变得未定义,这是JavaScript的一些限制,我猜你期待的是null而不是undefined,对吧?
A
Avi

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

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]

R
Roman Karagodin

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

const _ = Array.prototype;常量集合 = document.getElementById('ol').children; alert(_.reduce.call(collection, (acc, { textContent }, i) => { return acc += `${i+1}) ${textContent}` + '\n'; }, '') );

  1. foo
  2. bar
  3. bat
  4. baz