Javascript 何时使用节点转换器
比较QSA和Javascript 何时使用节点转换器,javascript,dom,dom4,Javascript,Dom,Dom4,比较QSA和.forEach与节点转换器 toArray(document.querySelectorAll("div > a.klass")).forEach(function (node) { // do something with node }); var filter = { acceptNode: function (node) { var condition = node.parentNode.tagName === "DIV" &&am
.forEach
与节点转换器
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
,要么就是我做错了
问题:我应该在什么时候使用节点编辑器
如果您不知道,DOM4会指定它是什么。由于各种原因,它的速度很慢。最明显的事实是,没有人使用它,只是花在优化它上的时间少得多。另一个问题是它的大量可重入性,每个节点都必须调用JS并运行filter函数 如果你看一下,你会发现我添加了一个迭代器使用
getElementsByTagName(“*”)
的重新实现,然后在上面运行一个相同的过滤器。结果表明,它的速度要快得多。走JS>C++ C++的速度很慢。
完全过滤JS(
querySelectorAll
使用的选择器匹配相对来说比较智能:它进行从右到左的匹配,并且基于预先计算的缓存(大多数浏览器将使用类“klass”遍历所有元素的缓存列表,检查它是否为a
元素,然后检查父元素是否为div
)因此,他们甚至不必对整个文档进行迭代
鉴于此,何时使用NodeIterator?至少在JavaScript中基本上从未使用过。在Java这样的语言中(毫无疑问,这是为什么会有一个名为NodeIterator的接口的主要原因),它很可能与其他任何语言一样快,因为这样您的过滤器将使用与过滤器相同的语言。除此之外,它唯一有意义的时间是在创建节点对象的内存使用量远远大于节点内部表示的语言中。由于各种原因,几乎从未使用过节点编辑器。这意味着关于这个主题的信息是稀缺的,而且是现成的,这完全没有抓住要点。我知道这个问题已经问了将近十年了,所以请原谅我的巫术
1.启动与执行
确实,NodeIterator
的启动比querySelectorAll
等方法慢,但这不是您应该测量的性能
关于NodeIterator
s的一点是,它们是实时的,就像HTMLCollection
或实时NodeList
一样,您可以在启动对象一次后继续使用它。由
querySelectorAll
返回的NodeList
是静态的,每次需要匹配新添加的元素时都必须重新启动
jsPerf的将节点运算符
放入准备代码中。实际测试仅尝试使用iter.nextNode()
循环所有新添加的元素。您可以看到迭代器现在快了几个数量级
2.选择器性能
好的,酷。缓存迭代器更快,然而,这表明了另一个显著的差异。我已经添加了10个选择器不应该匹配的类(done[0-9]
)。迭代器的速度损失约为10%,而查询选择器的速度损失为20%
另一方面,显示在选择器开始处添加另一个div>
时发生的情况。迭代器的速度损失了33%,而查询选择器的速度增加了10%
删除选择器开始处的初始div>
,如中所示,这两种方法都会变慢,因为它们比早期版本匹配得更多。与预期的一样,在本例中,迭代器的性能相对优于QuerySelector
这意味着在节点编辑器
中,基于节点自身属性(其类、属性等)的过滤速度可能更快,而在选择器中有许多组合符(>、+、~)可能意味着querySelectorAll
更快。这对于
(空格)组合符尤其如此。使用querySelectorAll('article a')
选择元素要比手动循环遍历每个a
元素的所有父元素,查找具有标记名的元素要容易得多
另外,在§3.2中,我举了一个例子,说明如果您希望与空格组合符的功能相反(排除带有文章的标签)
3个不可能的选择器
3.1简单的层次关系
当然,手动过滤元素实际上为您提供了无限的控制。这意味着您可以过滤掉通常无法与CSS选择器匹配的元素。例如,CSS选择器只能以如下方式“回头看”div
选择前面有另一个div
的div
。无法选择紧跟另一个div
的div
但是,在NodeFilter
中,您可以通过选中node.nextElementSibling.tagName==='DIV'
来实现这一点。CSS选择器无法做出的每个选择也是如此
3.2更多全球层级关系
我个人喜欢的另一件事是,通过返回NodeFilter.FILTER\u reject
而不是NodeFilter.FILTER\u SKIP
,可以拒绝节点及其整个子树
假设您想要迭代页面上的所有a
标记,除了那些具有文章
祖先的标记
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
集中