为什么在删除之前使用点表示法检查属性比直接删除属性更快?

时间:2014-03-21 17:25:17

标签: javascript performance dom syntax conditional

我问过this question,结果发现当从元素中删除属性时,首先使用elem.xxx!==undefined检查元素是否存在会使运行时更快。 Proof

为什么会更快? {1}}方法无论你采用哪种方式。

4 个答案:

答案 0 :(得分:5)

嗯,首先需要知道的是elem.xxxelem.getAttribute()或与属性相关的任何其他方法都不相同。

elem.xxx是DOM元素的属性,而DOM内部的HTML上的属性和元素都相似但不同。例如,请使用以下DOM元素:<a href="#">和此代码:

//Let say var a is the <a> tag
a.getAttribute('href');// == #
a.href;// == http://www.something.com/# (i.e the complet URL)

但是,请使用自定义属性:<a custom="test">

//Let say var a is the <a> tag
a.getAttribute('custom');// == test
a.custom;// == undefined

因此,您无法真正比​​较两者的速度,因为它们无法达到相同的效果。但是一个显然更快,因为属性是快速访问数据,而属性使用get / hasAttribute DOM函数。

现在,为什么没有条件更快?仅仅因为removeAttribute并不关心属性是否缺失,它会检查它是否属于。

因此在hasAttribute之前使用removeAttribute就像执行两次检查一样,但条件有点慢,因为它需要检查运行代码是否满足条件。

答案 1 :(得分:2)

我怀疑速度提升的原因是追踪树。

加州大学欧文分校的Andreas Gal和Michael Franz在他们的论文Incremental Dynamic Code Generation with Trace Trees中首次提出了追踪树。

在他的博客文章Tracing the Web中,Andreas Gal(该论文的合着者)解释了跟踪即时编译器的工作原理。

为了解释跟踪JIT编译器的可能性(因为我对该主题的了解并不深刻),跟踪JIT编译器会执行以下操作:

  1. 最初解释所有要运行的代码。
  2. 计算每个代码路径的执行次数(例如执行true语句的if分支的次数)。
  3. 当代码路径的获取次数大于预定义阈值时,代码路径将被编译为机器代码以加快执行速度(例如,我相信SpiderMonkey会执行多次执行的代码路径)。
  4. 现在让我们来看看您的代码并了解导致速度提升的原因:

    测试用例1:检查

    if (elem.hasAttribute("xxx")) {
        elem.removeAttribute("xxx");
    }
    

    此代码具有代码路径(即if语句)。请记住,跟踪JIT只会优化代码路径而不是整个函数。这就是我认为正在发生的事情:

    1. 由于JSPerf正在对代码进行基准测试,因此它被执行了不止一次(轻描淡写)。因此它被编译成机器代码。
    2. 然而,它仍然会导致对hasAttribute的额外函数调用的开销,这不是JIT编译的,因为它不是条件代码路径的一部分(花括号之间的代码)。
    3. 因此,虽然大括号内的代码很快,但条件检查本身很慢,因为它没有编译。这是解释。结果是代码很慢。
    4. 测试用例2:删除

      elem.removeAttribute("xxx");
      

      在此测试用例中,我们没有任何条件代码路径。因此,JIT编译器永远不会启动。因此代码很慢。

      测试用例3:检查(点表示法)

      if (elem.xxx !== undefined) {
          elem.removeAttribute("xxx");
      }
      

      这与第一个具有一个显着差异的测试用例相同:

      1. 条件检查是一种简单的非等价检查。因此,它不会产生函数调用的全部开销。
      2. 大多数JavaScript解释器通过假设两个变量的固定数据类型来优化这样的简单等价性检查。由于elem.xxxundefined的数据类型每次迭代都不会更改,因此优化会使条件检查更快。
      3. 结果是条件检查(虽然解释)不会显着减慢编译的代码路径。因此这段代码是最快的。
      4. 当然这只是我的猜测。我不知道JavaScript引擎的内部结构,因此我的答案不是规范的。但是我认为这是一个很好的教育猜测。

答案 2 :(得分:0)

您的证明不正确......

elem.class !== undefined始终评估为false,因此永远不会调用elem.removeAttribute("class"),因此,此测试将始终更快。

elem使用的正确属性为className,例如:

typeof elem.className !== "undefined"

答案 3 :(得分:0)

正如Karl-AndréGagnon指出的那样,访问 [native] JavaScript属性并调用 DOM函数/属性是两个不同的操作

某些 DOM属性通过DOM IDL公开为 JavaScript属性;这些 adhoc JS属性相同,并且需要DOM访问权限。此外,即使公开了 DOM属性,也与 DOM属性没有严格的关系!

例如,inputElm.value = "x"更新 DOM属性,即使该元素将显示并报告更新的值。如果目标要处理 DOM属性,唯一正确的方法是使用hasAttribute/setAttribute等。


我一直致力于为不同的函数调用推导出一个“公平”的微基准测试,但它相当困难,并且发生了不同优化的很多。这里my best result,我将用它来论证我的情况。

请注意,没有 ifremoveAttribute混淆了结果,我只关注 来访问DOM / JS属性。此外,我试图排除速度差异仅仅是由函数调用引起的声明,并且我分配结果以避免公然的浏览器优化。 YMMV。

观察:

  1. JS属性的访问是快速。这是预期的 1,2

  2. 调用函数可能会比直接属性访问 1 带来更高的成本,但不会像 DOM属性 DOM函数<一样慢/ em>的。也就是说,它不仅仅是一个“函数调用”,它使hasAttribute变得如此慢。

  3. DOM属性访问较慢比原生 JS属性访问;但是,DOM属性和浏览器之间的性能差别很大。我更新的微基准测试显示了DOM访问的趋势 - 无论是通过DOM属性还是DOM功能 - 可能比本机JS属性访问 2 慢。

    < / LI>

    回到顶端:在元素上访问非DOM [JS]属性与访问 DOM属性完全不同较少的 DOM属性,在同一元素上。正是这种根本区别,以及跨浏览器的方法之间的优化(或缺乏),这些都是观察到的性能差异的原因。


    1 IE 10做了一些聪明的技巧,其中假函数调用非常快(我怀疑调用已被删除),即使它具有糟糕的JS属性访问。但是,考虑到IE是一个异常值或仅仅强化了函数调用而不是引入了本质上较慢的行为,这并没有减损我的主要论点:它是 DOM 访问这基本上是慢的。

    2 我想说DOM属性访问 更慢,但FireFox对input.value做了一些惊人的优化(但不是img.src) 。这里发生了一些特殊的魔法。 Firefox不会优化 DOM属性访问。

    并且,不同的浏览器可能会显示完全不同的结果..但是,我不认为必须考虑使用ifremoveAttribute的任何“魔法”来至少隔离我相信的内容成为“性能问题”:实际使用DOM