何时使用 NodeIterator

2024-06-19

基准 http://jsperf.com/qsa-vs-node-iterator比较 QSA 和.forEach vs a NodeIterator

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 指定了什么节点迭代器 http://www.w3.org/TR/domcore/#nodeiterator is.


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选择器只能以选择的方式“回头看”divpreceded由另一个div可以与div + div。选择divfollowed由另一个div是不可能的。

然而,在一个NodeFilter,您可以通过检查来实现这一点node.nextElementSibling.tagName === 'DIV'。这同样适用于 CSS 选择器无法做出的每个选择。

3.2 更多全局层级关系

我个人喜欢使用的另一件事NodeFilters,是当传递给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

综上所述

NodeIterators and TreeWalkers 不应该针对大量循环进行实例化,并且绝对不应该替换一次性循环。出于所有意图和目的,它们只是跟踪节点列表/树的替代方法,后者还添加了一些方便的糖FILTER_REJECT.

两者都有一个主要优点NodeIterators and TreeWalker必须提供:

  • 生动活泼,如第 1 节中所述

但是,当出现以下任一情况时,请勿使用它们:

  • 它的实例只会使用一次/几次
  • 使用 CSS 选择器可以查询复杂的层次关系
    (i.e. body.no-js article > div > div a[href^="/"])
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

何时使用 NodeIterator 的相关文章

随机推荐

  • 方向改变时重新定位控件

    我知道自动布局可用于在方向改变时使尺寸和位置保持一致 当方向改变时 是否可以完全改变布局 例如 请查看下面的纵向模式下简单登录屏幕的线框 现在 如果我旋转设备 我想完全重新定位控件 这种事情可以用自动布局来完成吗 如果没有 我该怎么办 谢谢
  • 显然不明确的调用不会导致GCC上的编译错误

    我对 GCC 这样做感到惊讶not考虑调用foo 在下面的程序中存在歧义 include
  • 如何解决 xcode 一直编译所有内容的问题?

    我已经开始使用 XCode 它似乎可以工作 嗯 大部分 烦人的是它每次都会编译所有源文件 甚至是那些没有更改的文件 我正在掌握 openframeworks 每次都浪费时间编译 openframeworks 源文件 尽管它们没有改变 以下是
  • Unity 4.3 - 2D,如何以编程方式将精灵分配给对象

    我正在尝试创建一个对象 该对象将负责创建和显示不同的精灵 因此我想以编程方式直接访问资产 精灵 而不是在该对象下的层次结构中拖放精灵 有没有一种方法可以以编程方式创建一个新的精灵并分配我在资产文件夹中拥有的内容 我还想要一种数据结构 其中在
  • angularjs ng-repeat 在两个级别上但只有一个输出

    我有一个看起来像这样的大物体 scope marketplaces first example second example 我想做的是循环遍历大对象 如下所示 section ul li li ul section 在循环内部 再次循环每
  • 如果通过谓词则返回值,否则返回默认值

    如果某个值未通过谓词 我该如何替换它 为了显示 assert eq 3 5 but if v v lt 0 then 0 0 我以为会有一些东西Option or Result允许这样做 但我找不到它 我以为会有一些东西Option or
  • 将控制器操作处理为 JS 而不是 HTML

    所以我有以下形式 Follow 我试图
  • iOS推送通知:当应用程序处于后台时,如何检测用户是否点击了通知?

    关于这个主题有很多 stackoverflow 线程 但我仍然没有找到好的解决方案 如果应用程序不在后台 我可以检查launchOptions UIApplicationLaunchOptionsRemoteNotificationKey
  • 实现与扩展:何时使用?有什么不同?

    请用易于理解的语言进行解释或提供某些文章的链接 extends is for 延伸一类 implements is for 实施一个接口 接口和常规类之间的区别在于 在接口中您不能实现任何声明的方法 只有 实现 接口的类才能实现方法 C 中
  • 使用 JSON 文件动态更新 HTML 内容?

    我想创建一个 JS 循环 使用 jQuery 来查看 JSON 文件 并根据是否 div ids 与 JSON id 值匹配 这需要易于扩展并且无论有多少人都可以工作 div 添加了盒子 我有一个 HTML 文件 设置如下 div clas
  • 导入错误:无法导入名称“FFProbe”

    我无法获取ffprobe包 https github com simonh10 ffprobe在 Python 3 6 中工作 我使用 pip 安装它 但是当我输入import ffprobe it says Traceback most
  • TestNG并行执行

    我有 4 个 Test 方法 并且想每个方法运行 3 次 我想在 12 个线程中同时执行这一切 我创建了一个像这样的 testng xml 文件
  • C#:DataSet.readXML( "filepath" ) 如何处理包含对象内对象内对象的 XML 文件?

    我有一个 xml 文件 格式如下
  • 如何同时支持 IPv4 和 IPv6 连接

    我目前正在开发 UDP 套接字应用程序 需要构建支持 以便 IPV4 和 IPV6 连接可以将数据包发送到服务器 我希望有人能帮助我并为我指明正确的方向 我发现的大部分文档都不完整 如果您能指出 Winsock 和 BSD 套接字之间的任何
  • 如何在 Meteor 应用程序之间共享 MongoDB 集合?

    我希望能够为我的项目提供一个管理应用程序和一个客户端应用程序 理想情况下 我希望能够拥有一个共享的 MongoDB 集合 我怎样才能做到这一点 我尝试在两个不同的应用程序中创建具有相同名称的集合 但发现 Meteor 会将数据分开 知道我能
  • 使用非英语的通用语言? [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 在讨论最近项目的规范和功能要求时 我们正在与领域专家讨论荷兰语的会计术语 因为整个团队和客户都是以荷兰语为母语的人 当开发开始时 我们很自然地用英语实
  • com.android.ddmlib.AdbCommandRejectedException:设备离线(即使设备已连接)

    将 Android Studio 更新到 2 1 2 后 当我进行更改时 我多次收到以下错误 com android ddmlib AdbCommandRejectedException 设备离线 安装 APK 时出错 问题是设备从未连接且
  • CSS:多属性选择器

    我想设置 电子邮件 和 密码 类型的表单输入样式 但不设置其他任何样式 我正在想象类似以下的事情 input type email type password 然而 属性选择器的工作方式似乎将其解释为 输入 其中类型同时是 电子邮件 and
  • Z 算法背后的直觉

    Z算法是一种复杂度为O n 的字符串匹配算法 一种用例是从字符串 B 中查找字符串 A 的最长出现次数 例如 overdose from stackoverflow 将会 over 您可以通过使用组合字符串调用 Z 算法来发现这一点 ove
  • 何时使用 NodeIterator

    基准 http jsperf com qsa vs node iterator比较 QSA 和 forEach vs a NodeIterator toArray document querySelectorAll div gt a kla