【问题标题】:Why is checking for an attribute using dot notation before removing faster than removing the attribute outright?为什么在删除之前使用点符号检查属性比直接删除属性更快?
【发布时间】:2014-04-29 05:13:47
【问题描述】:

我问了this question,结果发现当从一个元素中移除一个属性时,先使用elem.xxx!==undefined检查该元素是否存在可以加快运行时间。 Proof.

为什么它更快?还有更多的代码需要处理,无论你采用哪种方式,你都必须遇到removeAttribute() 方法去吧。

【问题讨论】:

  • 在我看来,检查属性比调用方法更快。对我来说并不奇怪。在大多数其他语言中也是如此。
  • 但这不仅仅是检查一个属性,而是检查一个属性,然后调用一个方法来删除它。这比简单地删除属性而不检查要快。这让我大吃一惊。
  • 谢谢@Karl-AndréGagnon,正如你所知道的......我是 JS 新手,我会在睡觉和醒来后阅读.prop(希望如此)。但是即使el.xxx 会比hastAttribute 更快,为什么使用el.xxx 检查然后删除属性仍然比不检查删除属性更快?
  • 在你的 jsperf 中,它没有调用方法来删除它,因为你的 DOM 对象上不存在该属性,即使你将它添加到 DOM 对象中,它也只会在第一次出现性能基准和之后的迭代将消失。
  • @dayuloli - 你的新 jsPerf 也不能正常工作,因为 "class" 属性是由 elem.className 而不是 elem.class 引用的,因为 class 是 Javascript 中的保留字。在 jsPerf 中您必须做的第一件事是在衡量其性能之前确保代码实际工作。

标签: javascript performance dom syntax conditional


【解决方案1】:

嗯,首先你需要知道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)

但是让我们使用自定义属性:&lt;a custom="test"&gt;

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

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

现在,为什么没有条件会更快?仅仅因为removeAttribute 不在乎属性是否丢失,它会检查是否没有。

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

【讨论】:

  • 但是if 语句在 OP 的 jsPerf 中永远不会满足,因为属性/属性根本不存在于它们的 jsPerf 中,因此讨论 if 块内任何东西的速度是无关紧要的。 OP 的 jsPerf 只不过是一个测试,如果 if (elem.prop !=== undefined)if (elem.hasAttribute('prop'))
  • @jfriend00 False,if 需要处理,所以它只是慢了一点。
  • if 块内永远不会执行,因为if 条件永远不会评估为真。
  • @jfriend00 它从不满足是的,但它需要检查是否它满足,而只使用removeAttribute不必这样做。
  • @user2864740 - 这正是我在回答中所说的,您继续投反对票。整个 jsPerf 只是关于测试 hasAttribute 与直接属性检查,两者甚至不一定检查同一件事。
【解决方案2】:

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

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

在他的博文Tracing the WebAndreas Gal(论文的合著者)中解释了跟踪即时编译器的工作原理。

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

  1. 一开始所有要运行的代码都会被解释。
  2. 记录每个代码路径的执行次数(例如,执行if 语句的true 分支的次数)。
  3. 当采用代码路径的次数大于预定义的阈值时,代码路径会被编译为机器代码以加快执行速度(例如,我相信 SpiderMonkey 会执行多次执行的代码路径)。

现在让我们看一下您的代码并了解导致速度提升的原因:

测试用例 1:检查

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

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

  1. 由于 JSPerf 对代码进行了基准测试,因此它被多次执行(轻描淡写)。因此它被编译成机器码。
  2. 但是,它仍然会产生对 hasAttribute 的额外函数调用的开销,因为它不是条件代码路径(大括号之间的代码)的一部分,所以它不是 JIT 编译的。
  3. 因此,尽管花括号内的代码速度很快,但条件检查本身却很慢,因为它没有被编译。它被解释。结果就是代码很慢。

测试用例 2:移除

elem.removeAttribute("xxx");

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

测试用例 3:检查(点符号)

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

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

  1. 条件检查是一种简单的非等价检查。因此,它不会产生函数调用的全部开销。
  2. 大多数 JavaScript 解释器通过假定两个变量的固定数据类型来优化简单的等价检查。由于 elem.xxxundefined 的数据类型不会在每次迭代中都发生变化,因此这种优化使条件检查更快。
  3. 结果是条件检查(尽管已解释)不会显着减慢编译的代码路径。因此这段代码是最快的。

当然,这只是我的猜测。我不知道 JavaScript 引擎的内部结构,因此我的回答不规范。不过我认为这是一个有根据的猜测。

【讨论】:

  • 虽然我不确定这是否正确(正如你自己所说的,有根据的猜测)。这可以解释一切!
【解决方案3】:

你的证明不正确...

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

elem 上要使用的正确属性是 className,例如:

typeof elem.className !== "undefined"

【讨论】:

  • 是的。但是使用elem.xxx 的其他测试编辑也表明它更快。我现在会更新我的“证明”
【解决方案4】:

正如 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 函数那样慢。也就是说,让hasAttribute 变慢的不仅仅是“函数调用”。

  3. DOM 属性 访问比原生 JS 属性 访问;但是,DOM 属性和浏览器之间的性能差异很大。我更新的微基准测试显示了一种趋势,即 DOM 访问——无论是通过 DOM 属性还是 DOM 函数——可能比原生 JS 属性访问慢2

回到最顶端:访问元素上的非 DOM [JS] 属性与访问 DOM 属性有着根本的不同,很多DOM 属性,在同一个元素上。 正是这种根本差异以及跨浏览器的方法之间的优化(或缺乏优化)导致了观察到的性能差异。 p>


1 IE 10 做了一些巧妙的技巧,其中假函数调用非常快(我怀疑调用已被忽略),即使它具有糟糕的 JS 属性访问。然而,考虑到 IE 是一个异常值或仅仅强化函数调用是 not 是什么引入了固有的较慢的行为,并没有减损我的主要论点:它是 DOM 访问这从根本上来说更慢。

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

而且,不同的浏览器可能会表现出完全不同的结果.. 但是,我认为不必考虑使用 ifremoveAttribute 的任何“魔法”来至少隔离我认为是“性能问题”:实际上是在使用 DOM

【讨论】:

    猜你喜欢
    • 2014-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-18
    • 2014-04-11
    相关资源
    最近更新 更多