首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >何时使用NodeIterator

何时使用NodeIterator
EN

Stack Overflow用户
提问于 2011-10-29 19:52:07
回答 2查看 4.6K关注 0票数 17

基准测试比较QSA & .forEach与a NodeIterator

代码语言:javascript
复制
toArray(document.querySelectorAll("div > a.klass")).forEach(function (node) {
  // do something with node
});

var filter = {
    acceptNode: function (node) {
        var condition = node.parentNode.tagName === "DIV" &&
            node.classList.contains("klass") &&
            node.tagName === "A";

        return condition ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT
    }  
}
// FIREFOX Y U SUCK
var iter = document.createNodeIterator(document, NodeFilter.SHOW_ELEMENT, filter, false);
var node;
while (node = iter.nextNode()) {
    // do thing with node    
}

现在,要么NodeIterator很烂,要么我做错了。

问题:什么时候应该使用NodeIterator

如果您不知道,DOM4会指定NodeIterator是什么。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2011-10-30 14:14:48

由于种种原因,它的速度很慢。最明显的事实是,没有人如此简单地使用它,更少的时间用于优化它。另一个问题是它是大规模重入的,每个节点都必须调用JS并运行过滤器函数。

如果您查看基准第三版,您会发现我添加了迭代器使用getElementsByTagName("*")执行的任务的重新实现,然后在上面运行相同的过滤器。结果显示,它的速度要快得多。去JS -> C++ -> JS是慢的。

在JS ( getElementsByTagName大小写)或C++ ( querySelectorAll情况)中完全过滤节点要比重复跨越边界要快得多。

注意,querySelectorAll使用的选择器匹配也是比较聪明的:它进行从右到左的匹配,并且基于预先计算的缓存(大多数浏览器将遍历类"klass“中所有元素的缓存列表,检查它是否是a元素,然后检查父元素是否是div),因此它们甚至不需要迭代整个文档。

既然如此,什么时候使用NodeIterator?从根本上说,至少在JavaScript里是没有的。在诸如Java这样的语言中(无疑是存在一个名为NodeIterator的接口的主要原因),它可能和任何其他语言一样快,就像过滤器一样使用相同的语言。除此之外,它唯一有意义的地方是在语言中,创建Node对象的内存使用远远大于Node的内部表示。

票数 15
EN

Stack Overflow用户

发布于 2019-10-03 14:45:46

由于各种原因,NodeIterator (和TreeWalker )几乎从未被使用过。这就意味着关于这个主题的信息是稀缺的,而类似于“gsnedders”的答案则是完全没有意义的。我知道这个问题差不多有十年了,所以请原谅我的亡灵。

  1. 初始化&性能=确实,初始化querySelectorAll这样的方法慢,但这不是您应该测量的性能。

关于NodeIterator的问题是,它们是实时的,就像HTMLCollection或LiveNodeList一样,您可以在启动对象一次之后继续使用它。

NodeList返回的querySelectorAll是静态的,每次需要匹配新添加的元素时都必须重新启动。

这个版本 of jsPerf将NodeIterator放在准备代码中。实际测试只尝试使用iter.nextNode()循环所有新添加的元素。您可以看到,迭代器现在的数量级更快。

  1. 选择器性能=好,酷。缓存迭代器更快。然而,这个版本显示了另一个显著的差异。我添加了10个选择器不应该匹配的类(done[0-9])。迭代器的速度损失约为10%,而querySelectors损失20%

另一方面,这个版本显示了在选择器的开头添加另一个div >时会发生什么。迭代器的速度损失了33%,而querySelectors得到了10%的速度增加

在选择器开始时删除初始div > (如在这个版本中)显示,这两种方法都变得更慢,因为它们比早期版本匹配得更多。与预期一样,在这种情况下,迭代器相对于querySelectors具有更高的性能。

这意味着根据节点自身的属性(类、属性等)进行过滤。在NodeIterator中可能更快,而有许多组合子(>,+,~,等等)在您的选择器中,可能意味着querySelectorAll更快。

对于空间(空间)组合器来说尤其如此。使用querySelectorAll('article a')选择元素要比手动遍历每个a元素的所有父元素要容易得多,寻找一个具有tagName of 'ARTICLE'的元素。

在第3.2节中,我给出了一个例子,说明如果您想要与空间组合器相反的功能(不包括带有a祖先的article标记),那么完全相反的方法是正确的。

3个不可能的选择器

3.1简单的层次关系

当然,手动过滤元素实际上给了您无限的控制。这意味着您可以筛选出通常无法与CSS选择器匹配的元素。例如,CSS选择器只能“回首”,因为在div之前选择另一个div是可能的。选择后面跟着另一个divdiv是不可能的。

但是,在NodeFilter内部,您可以通过检查node.nextElementSibling.tagName === 'DIV'来实现这一点。CSS选择器无法做出的每一个选择都是如此。

3.2更全面的层次关系

关于NodeFilter的使用,我个人喜欢的另一件事是,当传递给TreeWalker时,可以通过返回NodeFilter.FILTER_REJECT而不是NodeFilter.FILTER_SKIP来拒绝节点及其整个子树。

假设您想遍历页面上的所有a标记,除了那些具有article祖先的标记。

在querySelectors中,您可以键入以下内容

代码语言:javascript
复制
let a = document.querySelectorAll('a')
a = Array.prototype.filter.call(a, function (node) {
  while (node = node.parentElement) if (node.tagName === 'ARTICLE') return false
  return true
})

NodeFilter中,只需键入以下内容

代码语言:javascript
复制
return node.tagName === 'ARTICLE' ? NodeFilter.FILTER_REJECT : // ✨ Magic happens here ✨
       node.tagName === 'A'       ? NodeFilter.FILTER_ACCEPT :
                                    NodeFilter.FILTER_SKIP

结论:

并不是每次需要迭代同一类型的节点时都启动API。遗憾的是,这个假设是在提出问题时做出的,而+500的回答(给予它更多的信任)甚至没有解决错误或NodeIterator的任何额外好处。

NodeIterator必须提供两个主要优势:

  • 如第1节所讨论的那样,活生生的
  • 高级过滤,如第3节所述 (我怎么强调NodeFilter.FILTER_REJECT示例有多有用)

但是,当下列任何一项为真时,请不要使用NodeIterator

  • 它的实例只能使用一次/几次。
  • 使用CSS选择器可以查询复杂的层次关系。 (即body.no-js article > div > div a[href^="/"])

很抱歉,答案很长:)

票数 34
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/7941288

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档