大多数 DOM 方法和集合属性实际上并不是数组,它们是集合:
NodeList
最近才得到forEach
(以及keys
其他一些数组方法)。HTMLCollection
没有也不会;事实证明,添加它们会破坏网络上的太多代码。
这两个NodeList
和HTMLCollection
的迭代,虽然,这意味着你可以遍历他们for-of
,他们扩展到一个数组通过传播([...theCollection]
)等,但如果你在浏览器中运行,其中NodeList
没有forEach
,它可能太旧,有什么ES2015+ 等功能for-of
或迭代。
由于NodeList
被指定为 have forEach
,您可以安全地对它进行 polyfill,而且很容易做到:
if (typeof NodeList !== "undefined" && NodeList.prototype && !NodeList.prototype.forEach) {
// Yes, there's really no need for `Object.defineProperty` here
NodeList.prototype.forEach = Array.prototype.forEach;
}
在这种情况下,直接赋值很好,因为enumerable
, configurable
, 和writable
都应该是true
并且它是一个值属性。(enumerable
这true
让我感到惊讶,但这就是它在 Chrome/Chromium/Edge/Etc.、Firefox、旧的 Legacy Edge 和 Safari 上的本地定义方式)。
在你自己的代码中,HTMLCollection
如果你愿意,你也可以这样做,只是要注意,如果你使用一些旧的 DOM 库,比如 MooTools 或 YUI 或其他类似的东西,如果你添加forEach
到HTMLCollection
.
正如我之前所说,NodeList
并且HTMLCollection
都被指定为可迭代的(因为这个 Web IDL 规则¹)。如果您遇到具有 ES2015+ 功能但由于某种原因不能使集合可迭代的浏览器,您也可以对其进行 polyfill:
if (typeof Symbol !== "undefined" && Symbol.iterator && typeof NodeList !== "undefined" && NodeList.prototype && !NodeList.prototype[Symbol.iterator]) {
Object.defineProperty(NodeList.prototype, Symbol.iterator, {
value: Array.prototype[Symbol.itereator],
writable: true,
configurable: true
});
}
(对于HTMLCollection
.也是如此。)
这是一个使用两者的现场示例,在(例如)IE11 上试试这个(虽然它只会演示forEach
),它NodeList
本身没有这些功能:
// Using only ES5 features so this runs on IE11
function log() {
if (typeof console !== "undefined" && console.log) {
console.log.apply(console, arguments);
}
}
if (typeof NodeList !== "undefined" && NodeList.prototype) {
// forEach
if (!NodeList.prototype.forEach) {
// Yes, there's really no need for `Object.defineProperty` here
console.log("Added forEach");
NodeList.prototype.forEach = Array.prototype.forEach;
}
// Iterability - won't happen on IE11 because it doesn't have Symbol
if (typeof Symbol !== "undefined" && Symbol.iterator && !NodeList.prototype[Symbol.iterator]) {
console.log("Added Symbol.iterator");
Object.defineProperty(NodeList.prototype, Symbol.iterator, {
value: Array.prototype[Symbol.itereator],
writable: true,
configurable: true
});
}
}
log("Testing forEach");
document.querySelectorAll(".container div").forEach(function(div) {
var html = div.innerHTML;
div.innerHTML = html[0].toUpperCase() + html.substring(1).toLowerCase();
});
// Iterable
if (typeof Symbol !== "undefined" && Symbol.iterator) {
// Using eval here to avoid causing syntax errors on IE11
log("Testing iterability");
eval(
'for (const div of document.querySelectorAll(".container div")) { ' +
' div.style.color = "blue"; ' +
'}'
);
}
<div class="container">
<div>one</div>
<div>two</div>
<div>three</div>
<div>four</div>
</div>
¹ 令人困惑,因为HTMLCollection
它是可迭代的,但它没有用iterable
声明标记,奇怪的是,在 JavaScript DOM 绑定中,这并不意味着某些东西是可迭代的,它意味着它具有forEach
, entries
, keys
, values
,并且它是可迭代的。但是HTMLCollection
,没有用iterable
声明标记的仍然是可迭代的。相反,由于前面提到的这个 Web IDL 规则,它是可迭代的。