为什么不能在 nodeList 上调用 forEach?

Why it is not possible to call forEach on a nodeList?

我正在使用 forEach 循环遍历 nodeList。我的代码如下

var array = document.querySelectorAll('items');

array.forEach(function (item) {
    console.log(item);
}); 

并且此代码抛出错误

Uncaught TypeError: array.forEach is not a function

然后在阅读了一些在线博客文章后,我将代码更改为这个。

[].forEach.call(array, (function (item) {
    console.log(item);
})); 

有人可以解释一下为什么不能在 nodeList 上调用 forEach 以及上面第二个代码片段的作用。 :)

编辑: 7/25/2017

此问题不适用于现代浏览器。您可以在其中的节点列表上使用 forEach

Although NodeList is not an Array, it is possible to iterate on it using forEach(). It can also be converted to an Array using Array.from().

However some older browsers have not yet implemented NodeList.forEach() nor Array.from(). But those limitations can be circumvented by using Array.prototype.forEach() (more in this document).

参考:MDN

NodeList 对象不包含方法 forEach,它是 Array object 的方法。下面的代码:

[].forEach.call(array, (function (item) {
    console.log(item);
})); 

正在使用数组中的 forEach 方法并将其传递给 NodeList

你有另一个选择,而且可以说更好,是将你的 NodeList 转换成数组,如下所示:

var myArrayOfNodes = [].slice.call(NodeList);

这使用数组对象 slice methodNodeList 创建节点数组。这是一个更好的方法,因为您可以使用数组而不是破解类似数组的对象

querySelectorAll 获取 array-like 对象中的元素而不是数组。所以你需要像第二个代码示例中那样使用。

这是 JavaScript 中的基本内容:您可以从一个对象中获取一个函数,然后 应用 到任何其他对象。即:将 this 设置为您 应用 函数的对象来调用它。这是可能的,因为在 JavaScript 中,所有 属性 名称等都是(简单地说)由名称标识的。因此,尽管 NodeList.lengthArray.length 不同,但函数 Array.forEach 可以 应用 到任何暴露 属性 length 的东西(以及 forEach 需要的其他内容)。

所以你的情况是这样的:

  • querySelectorAll() returns 类型为 NodeList 的对象,它恰好公开了 length 属性 并且是可枚举的(让我们说 [] 运算符可以访问它); NodeList 不公开 forEach 函数(如您所见,即此处:https://developer.mozilla.org/en-US/docs/Web/API/NodeList)——这就是为什么无法直接在结果上调用 forEach 的原因共 querySelectorAll()
  • [].forEach returns 一个函数 - 这是 不太聪明 Array.prototype.forEach
  • 的快捷方式
  • [].forEach.call(array, …) 此函数 应用array 引用的对象,NodeList[=60= 类型的对象](即 forEach 在函数体中用 array 作为 this 调用,所以当 forEach 里面有 this.length 它指的是 lengtharray 中,尽管 arrayNodeList 而不是真实的 Array)
  • 这是可行的,因为 forEach 使用的是 ArrayNodeList 的共同属性;如果 forEach 想使用 Array 有的一些 属性,但 NodeList 没有