为什么nodelist没有forEach?

Sna*_*fee 85 javascript arrays foreach dom

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

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

Dom*_*nic 89

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

请参阅MDN上的nodeList forEach().

原始答案

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

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

问题是代码错误地假定instanceof意味着实例是与Array.prototype.concat组合的Array.

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

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

if (x instanceof Array) {
  otherArray.concat(x);
} else {
  doSomethingElseWith(x);
}
Run Code Online (Sandbox Code Playgroud)

但是,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]
Run Code Online (Sandbox Code Playgroud)

所以这意味着上面的代码在xNodeList的时候就破了,因为在它走到doSomethingElseWith(x)路径之前,然后它走了otherArray.concat(x)路径,这做了一些奇怪的事,因为x它不是真正的数组.

有一段时间,有一个Elements类的提议是Array的真正子类,并将被用作"新的NodeList".但是,至少就目前而言,这已从DOM标准中删除,因为出于各种技术和规范相关的原因尚未实施.

  • 好像打电话给我.说真的,我认为这是一个偶然做出决定的正确决定,特别是如果它意味着我们将来有合适的API.此外,它不像网络甚至接近成为一个稳定的平台,人们习惯了他们2岁的javascript不再按预期运行.. (9认同)
  • "打破网络"!="打破谷歌的东西" (2认同)
  • 笔记; 此更新`NodeList 现在在所有主要浏览器中都有 forEach()` 似乎暗示 IE 不是主要浏览器。希望这对某些人来说是真的,但对我来说不是这样(目前)。 (2认同)

aku*_*uhn 55

你可以做

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

    // Your code here.

} );
Run Code Online (Sandbox Code Playgroud)

  • @CodeBrauer:这不只是缩短了`Array.prototype.forEach.call`,而是创建一个空数组并使用它的`forEach`方法. (6认同)
  • `Array.prototype.forEach.call`可以缩短为`[] .forEach.call` (3认同)

sbr*_*sbr 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.  

  });
Run Code Online (Sandbox Code Playgroud)

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

  nodes[0] === nodeList[0] // will be true
Run Code Online (Sandbox Code Playgroud)

  • 或者只做`Array.prototype.forEach.call(nodeList,fun)`. (21认同)

mai*_*man 19

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

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

  • Opera也支持它,并将在Firefox的v50中添加支持,计划于2016年11月15日发布. (2认同)

Mat*_* Lo 17

简而言之,它实现了该方法的设计冲突.

来自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?)

  • 那么,因为它是一个列表,为什么这样设计呢?什么阻止他们做链:`myNodeList - > NodeList.prototype - > Array.prototype - > Object.prototype - > null`? (8认同)

Ale*_*xTR 14

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

NodeList.prototype.forEach = Array.prototype.forEach;
Run Code Online (Sandbox Code Playgroud)

多数民众赞成,现在您可以像对待Array一样使用它:

document.querySelectorAll('td').forEach(function(o){
   o.innerHTML = 'text';
});
Run Code Online (Sandbox Code Playgroud)

  • 除了改变基类对普通读者不是很明确.换句话说,在某些代码中,您必须记住对浏览器基础对象进行的每个自定义.依赖于MDN文档不再有用,因为对象已从规范中更改了行为.最好在调用时明确应用原型,这样读者就可以很容易地意识到forEach是一个借来的想法,而不是语言定义的一部分.请参阅@akuhn上面的答案. (4认同)

Mis*_*bib 7

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

document.querySelectorAll('abbr').forEach( el => console.log(el));
Run Code Online (Sandbox Code Playgroud)

请参阅MDN链接

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

const elements = document.getElementsByTagName('abbr');
Array.from(elements).forEach( el => console.log(el));
Run Code Online (Sandbox Code Playgroud)

由于Array.from()方法是可调整的,因此您可以像这样在es5代码中使用它

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

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

检查当前的浏览器支持

要么

es2015的另一种方法是使用传播运算符。

[...document.querySelectorAll('abbr')].forEach( el => console.log(el));
Run Code Online (Sandbox Code Playgroud)

MDN点差算子

传播操作员-浏览器支持