【问题标题】:Why doesn't nodelist have forEach?为什么 nodelist 没有 forEach?
【发布时间】:2012-11-06 04:39:43
【问题描述】:

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

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

【问题讨论】:

标签: javascript arrays dom foreach


【解决方案1】:

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

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

MDN Link

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

查看current browser support

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

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

MDN spread operator

Spread Operator - Browser Support

【讨论】:

    【解决方案2】:

    检查 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
    });
    

    【讨论】:

      【解决方案3】:

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

      nodeList forEach() on MDN

      原答案

      这些答案都没有解释为什么 NodeList 不继承自 Array,因此允许它拥有 forEach 和所有其他的。

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

      问题是代码错误地假定 instanceof 表示该实例是与 Array.prototype.concat 组合的数组。

      Google 的 Closure 库中存在一个错误,导致几乎所有 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]
      

      所以这意味着当 x 是 NodeList 时,上面的代码会中断,因为在它沿着 doSomethingElseWith(x) 路径之前,它沿着 otherArray.concat(x) 路径,它做了一些奇怪的事情,因为 x 不是'不是一个真正的数组。

      一段时间以来,有人提出了Elements 类的提议,它是 Array 的真正子类,并将用作“新的 NodeList”。但是,至少目前是removed from the DOM Standard,因为由于各种技术和规范相关的原因,它还不可行。

      【讨论】:

      • 对我来说似乎是个坏电话。说真的,我认为偶尔打破一些东西是正确的决定,特别是如果这意味着我们未来拥有健全的 API。此外,它不像网络甚至接近成为一个稳定的平台,人们已经习惯了他们 2 岁的 javascript 不再按预期运行..
      • "破坏网络" != "破坏 Google 的东西"
      • 为什么不直接从 Array.prototype 中借用 forEach 方法呢?例如。而不是添加 Array 作为原型,只需在构造函数中执行 this.forEach = Array.prototype.forEach,甚至只是为 NodeList 唯一地实现 forEach?
      • 注意;此更新NodeList now has forEach() in all major browsers 似乎暗示 IE 不是主流浏览器。希望这对某些人来说是正确的,但对我来说(现在)还不是真的。
      • 奇怪的是,截至 2021 年 11 月,它仍未包含在 whatwg 规范中。
      【解决方案4】:

      我的解决方案:

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

      【讨论】:

      • 通过原型扩展 DOM 的功能通常不是一个好主意,尤其是在旧版本的 IE (article) 中。
      【解决方案5】:

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

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

      【讨论】:

      • Opera 也支持它,将在 Firefox 的 v50 中添加支持,计划于 2016 年 11 月 15 日发布。
      • 虽然已实现,但它不是任何标准的一部分。最好还是使用Array.prototype.slice.call(nodelist).forEach(…),这是标准的并且适用于旧浏览器。
      【解决方案6】:

      你可以的

      Array.prototype.forEach.call (nodeList, function (node) {
      
          // Your code here.
      
      } );
      

      【讨论】:

      • Array.prototype.forEach.call 可以缩写为[].forEach.call
      • @CodeBrauer:这不仅仅是缩短 Array.prototype.forEach.call,而是创建一个空数组并使用其 forEach 方法。
      【解决方案7】:

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

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

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

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

      【讨论】:

      • 除了基类的变异对普通读者来说不是很明确。换句话说,在某些代码的深处,您必须记住您对浏览器基础对象所做的每一次定制。依赖 MDN 文档不再有用,因为对象已经改变了规范的行为。最好在调用时显式应用原型,这样读者可以很容易地意识到 forEach 是一个借用的想法,而不是语言定义的一部分。请参阅上面@akuhn 的答案。
      • @Sukima 被错误的前提误导了。在这种特定情况下,给定的方法将 NodeList 修复为开发人员所期望的行为。这是修复系统类问题的最正确方法。 (NodeList 似乎未完成,应该在未来的语言版本中修复。)
      • 既然存在 NodeList.forEach,这将成为一个非常简单的 polyfill!
      【解决方案8】:

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

      来自 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
      【解决方案9】:

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

        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
      

      【讨论】:

      • 或者只是做Array.prototype.forEach.call(nodeList, fun)
      • 我还建议为 forEach 函数设置别名,例如:var forEach = Array.prototype.forEach.call(nodeList, callback);。现在您可以直接拨打forEach(nodeList, callback);
      【解决方案10】:

      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 绑定并遇到同样的问题。

      【讨论】:

      • 但是有什么特别的原因不应该实施吗? jQuery 和 Dojo 都在自己的库中实现了它
      • 但这怎么会是设计冲突呢?
      猜你喜欢
      • 2015-09-29
      • 2021-06-18
      • 1970-01-01
      • 2019-08-06
      • 2019-12-07
      • 2014-08-04
      • 1970-01-01
      • 1970-01-01
      • 2020-01-01
      相关资源
      最近更新 更多