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

Posted

技术标签:

【中文标题】在 ES6 中过滤或映射节点列表【英文标题】:Filter or map nodelists in ES6 【发布时间】:2015-12-22 06:45:09 【问题描述】:

的最有效方法是什么?

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

[...nodelist].filter

Array.from(nodelist).filter

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

【问题讨论】:

基本上,这两种方法都做同样的事情。如果您使用的是babel,那么[...coll] 将简单地调用Array.from(coll) 来处理不是Array 的任何内容。 FWIW,... 语法可能不受旧 IDE 支持,而 Array.from() 只是一种常规方法。 【参考方案1】: 如果对象是可迭代的,[...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) 不会。但是,如果您不映射列表,那也没关系。

【讨论】:

请注意,Array.from 不支持 IE。 @YannDìnendal 确实是这样,但是这个问题是在问 ES6,而且 IE 根本不支持 ES6,所以已经是死胡同了。 是的,这是真的。 :) 我应该澄清我的意思:如果您依赖 Babel 将 es6 转换为 es5,默认情况下它不会使用 polyfill 原型方法。所以只是需要注意的事情。 :)【参考方案2】:

TL;DR;

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

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

一样快

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

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

关于论点的简短说明

Array.prototype.slice(beginIndex, endIndex)

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

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

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

【讨论】:

感谢您提供此代码 sn-p,它可能会提供一些有限的即时帮助。一个适当的解释将通过展示为什么这是解决问题的好方法来极大地提高其长期价值,并使其对有其他类似问题的未来读者更有用。请编辑您的答案以添加一些解释,包括您所做的假设。 我想知道这是否支持 IE,因为 Array.from 不支持。是时候找一台IE机器了。现在我真的很困惑,因为我能够在 IE10 和 IE11 中使用 Array.from :\。此方法在 IE10+11 中确实有效,但当所有文档另有说明时,Array.from 工作并没有让我感到轻松。 Array.from 在 IE11 中对我不起作用 对象不支持“来自”的属性或方法 谢谢,这在 javascript 的旧实现上对我有用 Array.from 也返回一个浅拷贝。所以我看不出你是如何得出结论它比Array#slice运行得更快。【参考方案3】:

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

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

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

【讨论】:

【参考方案4】:

这个怎么样:

// 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。

【讨论】:

轻微警告,现在 nodeList.filter 会给你一个数组而不是一个节点列表。应该不是问题,但容易忘记^^【参考方案5】:

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

我是从这个简单的功能中走出来的。 @见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;

我愿意了解更多:>

【讨论】:

要 kip DOM 中的元素,您可以克隆它们并 kip 引用 Result.appendChild(NodeList.item(i).cloneNode)。接下来在你处理它们之前,你只需要回去说 dom,给我这种类型的具有这个属性的节点:: 重要!节点 === 节点列表....【参考方案6】:

[...a].filterArray.from(a).filter

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

性能 - 考虑 not 使用 either

但是为了性能(也为了避免“Array-ing”)你应该考虑为什么你过滤NodeListwhere/你是怎么得到它的。在许多情况下,您只需要 idclass 或其他 CSS 选择器的特定元素。

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

你甚至可以优化querySelectorAll或绕过“未知”的情况,如果你提供一个预存的父项来查看,让我举个例子:

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 倍,但比使用 id 预存储父级慢大约 2 倍。

除了更好的性能之外,您也总是会得到NodeList(它可能是空的,但它仍然是NodeList),这适用于document.querySelectorAll()Element.querySelectorAll()

【讨论】:

以上是关于在 ES6 中过滤或映射节点列表的主要内容,如果未能解决你的问题,请参考以下文章

ES6 数组map(映射)reduce(汇总)filter(过滤器)forEach(循环迭代)

MongoDb 聚合基于 ids 过滤列表并将此过滤列表映射到另一个字段

ES6 扩展运算符

es6数组属性

Grails + RESTful URL 映射 + 过滤器 + 路由

将 /xxx/yyy/zzz 动态映射到类 XxxYyyZzz.java 的 servlet 或过滤器