为什么nodelist没有forEach?

时间:2012-11-17 19:05:48

标签: javascript arrays dom foreach

我正在编写一个简短的脚本来更改<abbr>元素的内部文本,但发现nodelist没有forEach方法。我知道nodelist不会从Array继承,但看起来forEach似乎不是一个有用的方法吗?我不知道是否存在阻止将forEach添加到nodelist的特定实施问题?

注意:我知道Dojo和jQuery的节点列表都有某种形式的forEach。由于限制,我无法使用。

10 个答案:

答案 0 :(得分:82)

NodeList现在在所有主流浏览器中都有forEach()

请参阅nodeList forEach() on MDN

原始答案

这些答案都没有解释为什么 NodeList不会从Array继承,从而允许它拥有forEach以及所有其余的。

答案是on this es-discuss thread。简而言之,它破坏了网络:

  

问题是代码错误地假定instanceof意味着该实例是与Array.prototype.concat结合使用的Array。

     

Google的Closure Library中存在一个错误,导致几乎所有Google的应用都因此而失败。一旦发现这个库就更新了库但是可能仍然有代码与concat结合使用相同的错误假设。

也就是说,有些代码做了像

这样的事情
if (x instanceof Array) {
  otherArray.concat(x);
} else {
  doSomethingElseWith(x);
}

然而,concat会对待&#34;真实&#34;数组(不是instanceof Array)与其他对象不同:

[1, 2, 3].concat([4, 5, 6]) // [1, 2, 3, 4, 5, 6]
[1, 2, 3].concat(4) // [1, 2, 3, 4]

这意味着当x是一个NodeList时,上面的代码就破了,因为它在doSomethingElseWith(x)路径下降之前,然后它沿着otherArray.concat(x)路径向下走,它做了一些事情很奇怪,因为x并不是真正的数组。

有一段时间,有一个Elements类的提议,它是Array的一个真正的子类,并将被用作&#34;新的NodeList&#34;。但是,至少就目前来说,这是removed from the DOM Standard,因为出于各种技术和规范相关的原因而实施它是不可行的。

答案 1 :(得分:52)

你可以做到

Array.prototype.forEach.call (nodeList, function (node) {

    // Your code here.

} );

答案 2 :(得分:32)

您可以考虑创建一个新的节点数组。

  var nodeList = document.getElementsByTagName('div'),

      nodes = Array.prototype.slice.call(nodeList,0); 

  // nodes is an array now.
  nodes.forEach(function(node){ 

       // do your stuff here.  

  });

注意:这只是我们在这里创建的节点引用的列表/数组,没有重复的节点。

  nodes[0] === nodeList[0] // will be true

答案 3 :(得分:18)

永远不要说永远,它是2016年,NodeList对象在最新的chrome(v52.0.2743.116)中实现了forEach方法。

在生产中使用它还为时过早,因为其他浏览器尚不支持此功能(经过测试的FF 49),但我猜这很快就会标准化。

答案 4 :(得分:16)

简而言之,实现该方法存在设计冲突。

来自MDN:

  

为什么我不能在NodeList上使用forEach或map?

     

NodeList与数组非常相似,而且很有吸引力   对它们使用Array.prototype方法。但是,这是不可能的。

     

JavaScript具有基于原型的继承机制。排列   实例继承数组方法(例如forEach或map)因为它们的   原型链如下所示:

     

myArray --> Array.prototype --> Object.prototype --> null(   可以通过多次调用获得对象的原型链   Object.getPrototypeOf)

     

forEach,map等都是Array.prototype的属性   对象

     

与数组不同,NodeList原型链如下所示:

     

myNodeList --> NodeList.prototype --> Object.prototype --> null

     

NodeList.prototype包含item方法,但没有   Array.prototype方法,因此它们不能在NodeLists上使用。

来源:https://developer.mozilla.org/en-US/docs/DOM/NodeList(向下滚动到为什么我不能在NodeList上使用forEach或map?

答案 5 :(得分:13)

如果您想在NodeList上使用forEach,只需从Array中复制该函数:

NodeList.prototype.forEach = Array.prototype.forEach;

多数,现在您可以像对阵Array一样使用它:

document.querySelectorAll('td').forEach(function(o){
   o.innerHTML = 'text';
});

答案 6 :(得分:4)

在ES2015中,您现在可以使用forEach方法到nodeList。

document.querySelectorAll('abbr').forEach( el => console.log(el));

请参阅MDN Link

但是,如果要使用HTML集合或其他类似数组的对象,则在es2015中,您可以使用Array.from()方法。此方法采用类似数组或可迭代的对象(包括nodeList,HTML Collections,字符串等)并返回一个新的Array实例。您可以像这样使用它:

const elements = document.getElementsByTagName('abbr');
Array.from(elements).forEach( el => console.log(el));

由于Array.from()方法是可调的,您可以在es5代码中使用它,如

var elements = document.getElementsByTagName('abbr');
Array.from(elements).forEach( function(el) {
    console.log(el);
});

有关详细信息,请参阅MDN页。

检查current browser support

另一种es2015方式是使用扩展运算符。

[...document.querySelectorAll('abbr')].forEach( el => console.log(el));

MDN spread operator

Spread Operator - Browser Support

答案 7 :(得分:2)

我的解决方案:

//foreach for nodeList
NodeList.prototype.forEach = Array.prototype.forEach;
//foreach for HTML collection(getElementsByClassName etc.)
HTMLCollection.prototype.forEach = Array.prototype.forEach;

答案 8 :(得分:0)

NodeList是DOM API的一部分。查看适用于JavaScript的ECMAScript绑定。 http://www.w3.org/TR/DOM-Level-2-Core/ecma-script-binding.html。 nodeList和只读length属性以及item(index)函数返回一个节点。

答案是,你必须迭代。没有替代。 Foreach不起作用。 我使用Java DOM API绑定并遇到同样的问题。

答案 9 :(得分:0)

  

检查MDN是否符合NodeList.forEach规范。

NodeList.forEach(function(item, index, nodeList) {
    // code block here
});

在IE中,使用akuhn's answer

[].forEach.call(NodeList, function(item, index, array) {
    // code block here
});