102

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

注意:我知道 Dojo 和 jQuery 都有forEach某种形式的节点列表。由于限制,我不能使用任何一个。

4

10 回答 10

105

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

请参阅MDN 上的 nodeList forEach()

原始答案

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

答案在这个 es-discuss 线程上找到。简而言之,它破坏了网络:

问题是代码错误地假设 instanceof 意味着该实例是一个与 Array.prototype.concat 组合的数组。

Google 的 Closure Library 中存在一个错误,导致几乎所有 Google 的应用程序都因此而失败。发现此库后立即更新该库,但可能仍有代码与 concat 结合使用相同的错误假设。

也就是说,一些代码做了类似的事情

if (x instanceof Array) {
  otherArray.concat(x);
} else {
  doSomethingElseWith(x);
}

但是,concat会将“真实”数组(不是 instanceof Array)与其他对象区别对待:

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

所以这意味着上面的代码在xNodeList 是什么时候坏了,因为在它沿着doSomethingElseWith(x)路径之前,而在之后它沿着otherArray.concat(x)路径,这做了一些奇怪的事情,因为x它不是一个真正的数组。

一段时间以来,有人提议创建Elements一个真正的 Array 子类,并将其用作“新的 NodeList”。但是,至少目前,它已从 DOM 标准中删除,因为由于各种技术和规范相关的原因,实现它还不可行。

于 2014-11-19T18:25:16.593 回答
62

你可以做

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

    // Your code here.

} );
于 2014-03-14T22:07:34.883 回答
34

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

  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
于 2013-03-13T22:58:38.340 回答
20

永远不要说永远,现在是 2016 年,并且该NodeList对象已经forEach在最新的 chrome (v52.0.2743.116) 中实现了一个方法。

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

于 2016-08-24T21:25:09.793 回答
19

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

来自 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 (向下滚动到为什么我不能使用 forEach 或在 NodeList 上映射?

于 2012-11-17T19:10:05.657 回答
15

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

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

就是这样,现在您可以像使用 Array 一样使用它:

document.querySelectorAll('td').forEach(function(o){
   o.innerHTML = 'text';
});
于 2014-05-31T18:30:24.273 回答
9

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

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

查看MDN 链接

但是如果你想使用 HTML Collections 或其他类似数组的对象,在 es2015 中,你可以使用Array.from()method. 此方法接受一个类似数组或可迭代的对象(包括 nodeList、HTML 集合、字符串等)并返回一个新的 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页面。

检查当前浏览器支持

或者

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

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

MDN 传播算子

扩展运算符 - 浏览器支持

于 2018-06-15T05:31:17.103 回答
1

我的解决方案:

//foreach for nodeList
NodeList.prototype.forEach = Array.prototype.forEach;
//foreach for HTML collection(getElementsByClassName etc.)
HTMLCollection.prototype.forEach = Array.prototype.forEach;
于 2017-11-12T19:14:46.287 回答
0

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

答案是,你必须迭代。没有替代。Foreach 将无法正常工作。我使用 Java DOM API 绑定并遇到同样的问题。

于 2012-11-17T19:14:27.310 回答
0

检查 MDN 以获取NodeList.forEach规范。

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

在 IE 中,使用akuhn 的答案

[].forEach.call(NodeList, function(item, index, array) {
    // code block here
});
于 2018-10-01T23:04:52.953 回答