我正在编写一个简短的脚本来更改<abbr>
元素的内部文本,但发现nodelist
没有forEach
方法。我知道nodelist
没有继承自Array
,但是forEach
似乎是一个有用的方法,难道不能将forEach
添加到nodelist
中吗?是否有我不知道的特定实现问题阻止了将forEach
添加到nodelist
中?
注意:我知道Dojo和jQuery都以某种形式为它们的节点列表提供forEach
。由于存在限制,我无法使用任何一个。
我正在编写一个简短的脚本来更改<abbr>
元素的内部文本,但发现nodelist
没有forEach
方法。我知道nodelist
没有继承自Array
,但是forEach
似乎是一个有用的方法,难道不能将forEach
添加到nodelist
中吗?是否有我不知道的特定实现问题阻止了将forEach
添加到nodelist
中?
注意:我知道Dojo和jQuery都以某种形式为它们的节点列表提供forEach
。由于存在限制,我无法使用任何一个。
这些答案都没有解释为什么NodeList不继承自Array,因此它可以拥有forEach
和其他所有功能。
答案可以在这个es-discuss线程中找到。简而言之,它会破坏网络:
问题在于错误地假设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”。然而,由于技术和规范相关的原因,它目前已经从 DOM 标准中被移除,至少暂时无法实现。你可以做到
Array.prototype.forEach.call (nodeList, function (node) {
// Your code here.
} );
Array.prototype.forEach.call
缩写为[].forEach.call
。 - CodeBrauerArray.prototype.forEach.call
的写法,他还创建了一个空数组并使用其 forEach
方法。 - Paul D. Waite你可以考虑创建一个新的节点数组。
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)
。 - akuhnvar forEach = Array.prototype.forEach.call(nodeList, callback);
。现在你只需要调用 forEach(nodeList, callback);
即可。 - Andrew Craswell永远不要说永远,现在是2016年,在最新的Chrome浏览器(v52.0.2743.116)中,NodeList
对象已经实现了一个forEach
方法。
尽管其他浏览器还不支持它(测试FF 49),但现在使用它还为时过早,但我猜这很快就会被标准化。
Array.prototype.slice.call(nodelist).forEach(…)
,这是标准的,在旧浏览器中也能运行。 - Nate简而言之,这是一个设计冲突,无法在NodeList上实现该方法。
来自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/zh-CN/docs/Web/API/NodeList (滚动到为什么我不能在NodeList上使用forEach或map?)
myNodeList --> NodeList.prototype --> Array.prototype --> Object.prototype --> null
? - Igor Pantović如果你想在NodeList上使用forEach,只需从Array中复制该函数即可:
NodeList.prototype.forEach = Array.prototype.forEach;
好了,现在您可以像对待数组一样使用它:
document.querySelectorAll('td').forEach(function(o){
o.innerHTML = 'text';
});
forEach
方法来遍历nodeList。document.querySelectorAll('abbr').forEach( el => console.log(el));
请查看MDN链接
但是如果您想要使用HTML集合或其他类似数组的对象,在es2015中,您可以使用Array.from()
方法。该方法接受一个类数组或可迭代对象(包括节点列表、HTML集合、字符串等),并返回一个新的数组实例。您可以像这样使用它:
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页面。
检查当前浏览器支持情况。
OR
另一种使用es2015的方法是使用展开运算符。
[...document.querySelectorAll('abbr')].forEach( el => console.log(el));
//foreach for nodeList
NodeList.prototype.forEach = Array.prototype.forEach;
//foreach for HTML collection(getElementsByClassName etc.)
HTMLCollection.prototype.forEach = Array.prototype.forEach;
你可以只用一行代码为旧浏览器添加forEach polyfill:
window.NodeList && !NodeList.prototype.forEach && (NodeList.prototype.forEach = Array.prototype.forEach);
NodeList.forEach(function(item, index, nodeList) {
// code block here
});
[].forEach.call(NodeList, function(item, index, array) {
// code block here
});
Array.prototype.forEach
就已经存在了,事实上,NodeList.prototype.forEach === Array.prototype.forEach
。但是这个事实并没有在 ECMAScript 中被 _指定_,而是在 WebIDL 中被指定的。 - Sebastian Simon.forEach()
,getElementsByClassName()
返回一个没有.forEach()
的 HTMLCollection,可以使用返回 nodeList 的 querySelectorAll('.classname')。 - DWB