使用forEach方法迭代NodeList

时间:2014-02-20 22:40:58

标签: javascript foreach nodelist slice htmlcollection

我想使用forEach方法迭代NodeList,我搜索了该方法,我发现解决方法是将NodeList转换为数组:

var nodesArray = Array.prototype.slice.call(nodeList);

但我不明白为什么我们使用slice方法?

4 个答案:

答案 0 :(得分:10)

迭代NodeList的方法有很多种。

通常,将nodeList转换为Array或使用Array函数引用nodeList不是一个好主意。

你可以使用一个好的老式for循环,从零开始循环,直到我们到达数组的末尾。这种方法已经存在,并且至今仍在使用。这种方法在某种程度上比这里提到的其他方法更不易读,但这一切都归结为更容易编写的内容。

for(var i = 0; i < nodeList.length; ++i)  doSomething(nodeList[i]);

有些人会告诉你,使用向后循环将节省&#34;计算周期&#34;,无论真正意味着什么。实际上,默认情况下,某些IDE实际上会将前一个循环转换为以下结构。

实际上,微观优化是不受欢迎的。 此方法与此处提及的其他方法之间的性能没有实际的明显差异。

for(var i = nodeList.length - 1; i > -1; --i)  doSomething(nodeList[i]);

您可以使用while循环,它需要条件语句作为其参数。如果NodeList.item(n)超过NodeList的边界,它将返回null,这将结束循环。

var i = 0;
while((node = nodeList.item(i++))) doSomething(node);

另一种使用for循环的方法,类似于while循环。传统for循环的中间条件需要一个条件语句,就像上面的while循环一样,所以这是有效的。

for(var i = 0; (node = nodeList.item(i)); i++) doSomething(node);

您可以在Object.keys()中使用for ... in循环。请注意,在使用for ... in循环时必须使用Object.keys,否则它将迭代不可枚举的属性以及可枚举的属性。

  

Object.keys()方法返回给定对象自己的可枚举属性的数组,其顺序与for ... in循环提供的顺序相同(不同之处在于for-in循环枚举原型链中的属性。)    来自: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys

for(var i in Object.keys(nodeList))  doSomething(nodeList[i]);

在ES6中,您可以通过从Array()检索Iterator函数并将其应用于NodeList来使用for ... of循环。这也适用于对象的大多数其他用途,只要属性是可枚举的。

nodeList[Symbol.iterator] = [][Symbol.iterator];
for(node of nodeList) doSomething(node);

另外,很酷,以下适用于ES6。你在这里做的是从Array中检索Symbol.iterator函数并将其应用于NodeList对象的原型。这样,无论何时创建新的NodeList,它都将始终具有迭代器,因此您只需在脚本的开头执行此操作。

&#13;
&#13;
(function(){
    "use strict";
    NodeList.prototype[Symbol.iterator] = [][Symbol.iterator];
    for(let node of document.querySelectorAll('*')) document.body.innerHTML += node.tagName + ' ';
    document.body.innerHTML += '<br>';
    for(let node of document.querySelectorAll('*:not(body)')) document.body.innerHTML += node.tagName + ' ';
})();
&#13;
&#13;
&#13;

前面提到的每种方法都有变化,并且可能还有其他方法我在这里没有记录,但上面介绍的方法可以让您大致了解如何在不欺骗的情况下迭代NodeList数组函数。

  

我想使用forEach方法

迭代NodeList

为什么呢?这是真正的问题。当你可以通过欺骗数组函数来相信NodeList是一个数组来迭代NodeList时,你为什么要这样做?

  

我搜索了该方法,我发现解决方法是将NodeList转换为数组。

是的,这是一种劫持Array函数以迭代NodeList的方法。

  

但我不明白为什么我们使用切片方法?

Array.prototype.slice()返回原始数组中的浅层元素。使用.call()可以让我们为this的NodeList补充slice()的值。

Array.prototype.slice.call(NodeList)返回一个数组,表示对象的可枚举属性的浅层副本

有许多方法可以劫持数组函数并诱使他们认为他们正在使用数组同时为它们提供一个对象。

我个人认为以下任何一种方法都是可怕的代码但是,因为你问过它,我将包括可以在NodeList上使用的任何相关的数组函数。

Array.prototype.slice.call(nodeList).forEach(doSomething);
[].slice.call(nodeList).forEach(doSomething);

您不需要从NodeList中创建数组,您可以直接在.call()函数上使用forEach

Array.prototype.forEach.call(nodeList, doSomething);
[].forEach.call(nodeList, doSomething);

或者你可以使用map根据公式从nodeList创建一个新数组,然后使用forEach迭代NodeList。

Array.prototype.map.call(nodeList, function(item) { 
    return item; 
}).forEach(doSomething);
[].map.call(nodeList, function(item) { 
    return item; 
}).forEach(doSomething);

这些工作是因为Array是一种Object类型,并且NodeList与Array类似,您可以将一些Array函数与NodeList一起使用。

但是,不应该鼓励这些方法,有更好的方法来迭代NodeList

最后,如果你不想劫持任何数组函数,但想要使用forEach,你可以从NodeList构建一个数组。

var array = new Array(els.length);
for(var key in Object.keys(nodeList)) arrary[key] = nodeList[key];
array.forEach(doSomething);

这有点多余,因为你基本上是迭代NodeList两次。

这里要注意的重要一点是,就性能而言,这些方法之间没有真正的切实差异。所有这些方法都会以相对相似的速度执行,如果你达到了迭代足够元素的程度, 实际上很重要,那么你应该问自己是否有更好的方法,你应该正在采取解决您的问题。

至于哪种方法比其他方法更好,这一切都取决于你更容易理解的内容以及你最舒服的写作方式。

JSPerf results

答案 1 :(得分:3)

  

使用forEach方法

迭代NodeList      

但我不明白为什么我们使用切片方法?

你不必,你可以直接这样做

Array.prototype.forEach.call(nodelist, function(value, index) {
    ...
});

答案 2 :(得分:0)

因为slice将任何类似数组的参数的副本作为新数组对象返回,这正是我们所需要的。我们可以轻松使用concat

答案 3 :(得分:-1)

所有这些答案都已过时。实际上你可以在现代浏览器的NodeList上使用forEach

所以只需使用forEach