在 ES6 中过滤或映射节点列表

Filter or map nodelists in ES6

在 ES6 中过滤或映射节点列表的最有效方法是什么?

根据我的阅读,我会使用以下选项之一:

[...nodelist].filter

Array.from(nodelist).filter

你会推荐哪一个?是否有更好的方法,例如不涉及数组?

    如果对象是可迭代的,
  • [...nodelist] 将创建一个对象数组。
  • Array.from(nodelist) 如果对象是可迭代的 如果对象是类数组(具有 .length 和数字道具)

如果 NodeList.prototype[Symbol.iterator] 存在,你的两个例子将是相同的,因为这两种情况都包含可迭代对象。如果您的环境尚未配置为 NodeList 是可迭代的,那么您的第一个示例将失败,而第二个将成功。 Babel 目前 does not handle this case properly

因此,如果您的 NodeList 是可迭代的,那么使用哪种完全取决于您。我可能会根据具体情况进行选择。 Array.from 的一个好处是它接受映射函数的第二个参数,而第一个 [...iterable].map(item => item) 必须创建一个临时数组,而 Array.from(iterable, item => item) 则不需要。但是,如果您不映射列表,那没关系。

TL;DR;

Array.prototype.slice.call(nodelist).filter

slice() 方法 returns 一个数组。 返回的数组是集合 (NodeList) 的浅表副本 所以它比 Array.from() 工作得更快 所以它的工作速度和 Array.from()

一样快

原始集合的元素被复制到返回的数组中,如下所示:

  • 对于对象引用(而不是实际对象),slice 将对象引用复制到新数组中。原始数组和新数组都引用同一个对象。如果引用的对象发生更改,则更改对新数组和原始数组都是可见的。
  • 对于字符串、数字和布尔值(不是字符串、数字和布尔对象),slice 将值复制到新数组中。对一个数组中的字符串、数字或布尔值的更改不会影响另一个数组。

关于参数的简短说明

Array.prototype.slice(beginIndex, endIndex)

  • 采用可选参数 beginIndex 和 endIndex。 如果没有提供,切片使用 beginIndex == 0,因此它从集合中提取所有项目

Array.prototype.slice.call(namespace, beginIndex, endIndex)

  • 将一个对象作为第一个参数。如果我们将集合用作对象,它的字面意思是我们直接从该对象调用 slice 方法 namespace.slice()

我发现一个 reference 直接在 NodeList 上使用 map

Array.prototype.map.call(nodelist, fn)

我还没有测试过,但是这似乎会更快,因为它应该直接访问 NodeList。

这个怎么样:

// Be evil. Extend the prototype.
if (window.NodeList && !NodeList.prototype.filter) {
  NodeList.prototype.filter = Array.prototype.filter;
}

// Use it like you'd expect:
const noClasses = document
  .querySelectorAll('div')
  .filter(div => div.classList.length === 0)

它与 the MDN docs for NodeList.forEach(在 'Polyfill' 下)中提到的方法相同,适用于 IE11、Edge、Chrome 和 FF .

在 ES6 中过滤或映射节点列表

我就是从这个简单的函数中走出来的。 @see https://developer.mozilla.org/fr/docs/Web/API/NodeList/entries#exemple

function filterNodeList(NodeList, callback) {
if (!typeof callback === "function") callback = (i) => i; // Any have better idear?

const Result = document.createElement("div");
//# No need to filter empty NodeList
if (Node.length === 0) return Node;

for (let i = 0; i < Node.length; i++) {
  if (callback(Node.item(i))) Result.appendChild(Node.item(i));
}

return Result.childNodes;}

我愿意学习更多:>

[...a].filter 对比 Array.from(a).filter

不是“真正的”性能差异,Array.from 可能会快一点点,因为您没有在“JS 级别”上创建新的 Array,但它发生了直接在本机代码中。

性能 - 考虑使用或者

但是对于性能(并且为了避免“Array-ing”)你应该考虑为什么你过滤NodeListwhere/how 你明白了吗?在许多情况下,您只需要通过 idclass 或其他 CSS 选择器来选择特定元素。

document.querySelectorAll 就像 10 倍 - 200 倍快 并且适用于任何 CSS 选择器
document.getElementById 更快(但当然需要 id

你甚至可以优化 querySelectorAll 或绕过“not-yet-known”案例,如果你提供 pre-stored parent 来查看,让我举个例子:

let mainbar = document.getElementById('mainbar');
mainbar.querySelectorAll('.flex--item');

几乎比

快 10 倍
Array.from(a).filter(el => el.classList.contains("flex--item"))

另请注意,document.querySelectorAll('#mainbar .flex--item'); 仍然比 Array 过滤快约 5 倍,但比 pre-storing 和 parent 的 id 慢约 2 倍.

除了更好的性能,你 也总是会得到 NodeList(它可能是空的,但它仍然是 NodeListdocument.querySelectorAll() and Element.querySelectorAll()

使用 ECMAS 2016:

let nodes = [...document.querySelector('__SELECTOR__').childNodes].filter(item => item.nodeType === 1);