NodeIterator
(and TreeWalker
,就此而言)由于各种原因几乎从未被使用过。这意味着有关该主题的信息很少并且类似@gsnedders 的答案 https://stackoverflow.com/a/7945404/3791358来了,但没有提及 API 的任何独特功能。我知道这个问题已经有近十年的历史了,所以请原谅我的死灵术。
1. 启动与执行
确实,引发 of a NodeIterator
比这样的方法慢得多querySelectorAll
,但这不是您应该衡量的性能。
事情是关于NodeIterator
重要的是它们是活生生的,就像HTMLCollection
或住NodeList
,启动一次后可以继续使用该对象。
The NodeList
由返回querySelectorAll
是静态的,每次需要匹配新添加的元素时都必须重新启动。
这个版本 https://jsperf.com/qsa-vs-node-iterator/17jsPerf 的把NodeIterator
在准备代码中。实际测试仅尝试循环所有新添加的元素iter.nextNode()
。您可以看到迭代器现在速度快了几个数量级。
2. 选择器性能
好吧,酷,caching启动后的迭代器使该示例比查询更快。不过,如何使用 API 对于迭代速度仍然很重要。在这个版本中 https://jsperf.com/qsa-vs-node-iterator/18,您可以观察到另一个显着差异。我添加了 10 个课程(done[0-9]
)选择器不应该匹配。迭代器损失了大约10%其速度,而 querySelectors 则丢失20%.
这个版本 https://jsperf.com/qsa-vs-node-iterator/20另一方面,显示了当您添加另一个时会发生什么div >
在选择器的开头。迭代器丢失33%它的速度,而 querySelectors 的速度INCREASE of 10%.
Removing最初的div >
在选择器的开头,例如这个版本 https://jsperf.com/qsa-vs-node-iterator/21表明这两种方法都变得更慢,因为它们比早期版本匹配更多。正如预期的那样,在这种情况下,迭代器比 querySelector 的性能相对更高。
这意味着根据节点自身的属性(其类、属性等)进行过滤可能会更快NodeIterator
,虽然你的选择器中有很多组合符(>、+、~等)可能意味着querySelectorAll
是比较快的。
对于
(空间)组合器。选择元素querySelectorAll('article a')
比手动循环每个的所有父母要容易得多a
元素,寻找一个具有tagName
of 'ARTICLE'
.
附:在§3.2中,我给出了一个例子,说明如果您想要与空间组合器所做的相反的事情,则完全相反的情况是正确的(exclude a
带有一个标签article
祖先)。
3. 不可能的选择器
3.1 简单的层次关系
当然,手动过滤元素实际上可以为您提供无限的控制。这意味着您可以过滤掉通常无法与 CSS 选择器匹配的元素。例如,CSS选择器只能以选择的方式“回头看”div
是preceded由另一个div
可以与div + div
。选择div
是followed由另一个div
是不可能的。
然而,在一个NodeFilter
,您可以通过检查来实现这一点node.nextElementSibling.tagName === 'DIV'
。这同样适用于 CSS 选择器无法做出的每个选择。
3.2 更多全局层级关系
我个人喜欢使用的另一件事NodeFilter
s,是当传递给TreeWalker
,您可以通过返回来拒绝节点及其整个子树NodeFilter.FILTER_REJECT
代替NodeFilter.FILTER_SKIP
.
想象一下您想要迭代所有内容a
页面上的标签,除了带有article
祖先。
使用 querySelectors,您可以输入类似的内容
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
,你只需输入这个
return node.tagName === 'ARTICLE' ? NodeFilter.FILTER_REJECT : // ✨ Magic happens here ✨
node.tagName === 'A' ? NodeFilter.FILTER_ACCEPT :
NodeFilter.FILTER_SKIP
综上所述
NodeIterator
s and TreeWalker
s 不应该针对大量循环进行实例化,并且绝对不应该替换一次性循环。出于所有意图和目的,它们只是跟踪节点列表/树的替代方法,后者还添加了一些方便的糖FILTER_REJECT
.
两者都有一个主要优点NodeIterator
s and TreeWalker
必须提供:
但是,当出现以下任一情况时,请勿使用它们:
- 它的实例只会使用一次/几次
- 使用 CSS 选择器可以查询复杂的层次关系
(i.e. body.no-js article > div > div a[href^="/"]
)