【问题标题】:What is the cost of '$(this)'?'$(this)' 的成本是多少?
【发布时间】:2012-05-13 01:32:59
【问题描述】:

这里的人们经常建议缓存从DOM 元素创建的jQuery 对象,就像下面的代码:

$('#container input').each(function() {
    $(this).addClass('fooClass');
    $(this).attr('data-bar', "bar");
    $(this).css('background-color', 'red');
});
  • 缓存 jQuery 对象真的能提高我们代码的性能吗?
  • 当您将 DOM 元素传递给 jQuery 构造函数时,“幕后”会发生什么?

【问题讨论】:

  • 你应该总是缓存,但在这个特定的例子中,你甚至不需要这样做。只需利用 jQuery 链接:$(this).addClass('fooClass').attr('data-bar', "bar").css('background-color', 'red');

标签: javascript jquery performance


【解决方案1】:

在 jQuery tag info 中出现此警告:

jQuery 函数 $() 很昂贵。重复调用它效率极低。

嗯...这仅适用于字符串选择器,它会被正则表达式解析以找出它们是什么:

quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/

然后如果字符串是一个选择器(id 除外),jQuery 会遍历 DOM 以找到与它昂贵的 find 函数的匹配:

} else if ( !context || context.jquery ) {
    return ( context || rootjQuery ).find( selector );
}

是的,它很贵,但这仅适用于选择器!

如果我们传递DOMElement,jQuery 所做的唯一操作就是将 DOMElement 参数保存为新创建的 jQuery 对象的上下文,并将上下文的长度设置为 1:

// Handle $(DOMElement)
if ( selector.nodeType ) {
    this.context = this[0] = selector; // Selector here is a DOMElement
    this.length = 1;
    return this;
}

我做了some tests with jsPerf,发现缓存jQuery对象确实作用不大:

在 Chrome 中仅慢 7%。 (在 IE 中它更重要一点:12%。)

【讨论】:

  • 在任何一种情况下,您每次都至少保存一个函数调用。
  • 对比不对...性能有很大差异。
【解决方案2】:

要回答您的第二个问题,请查看source

// Handle $(DOMElement)
if ( selector.nodeType ) {
    this.context = this[0] = selector;
    this.length = 1;
    return this;
}

【讨论】:

  • 这是一个不错的查看源码的小应用:james.padolsey.com/jquery/#v=git&fn=jQuery.fn.init
  • 现在字符串选择器显然会有一个非常不同的图表。
  • 感谢 SLaks 将我指向 jQuery 源代码。我不知道为什么我自己一开始不这样做。
【解决方案3】:

关于性能差异,如果您正在寻找两者之间的直接比较,删除任何可能会影响结果的额外代码会很有帮助,例如 DOM 选择和其他不直接相关的方法。

http://jsperf.com/this-cost/2

在更真实的环境中,您的测试表明,相对差异很小

要记住的另一件事是,每次创建 jQuery 对象时,都需要为其分配内存,这增加了垃圾收集器需要做的工作。

所以我认为人们建议缓存的原因是从某种原则的角度来看。正在进行的额外工作虽然通常不会产生明显的影响,但最终确实需要一些可以轻松避免的开销。

【讨论】:

  • 这个比较比接受的答案的比较好得多。
【解决方案4】:

这里的所有运行时性能测试都忽略了一个主要考虑因素:

网络带宽。

$(this) 缓存到局部变量中通常会减小脚本的大小,尤其是在缩小脚本时(因为this 不能从四个字符减少)。

考虑:

function hello(text) {
    $(this).attr();
    $(this).css();
    $(this).data();
    $(this).click();
    $(this).mouseover();
    $(this).mouseleave();
    $(this).html(text);
}
hello('Hello world');

闭包编译器的缩小输出是

function hello(a){$(this).attr();$(this).css();$(this).data();$(this).click();$(this).mouseover();$(this).mouseleave();$(this).html(a)}hello("Hello world");

这节省了 39 个字节 (20%)。现在考虑:

function hello(name) {
    var $this = $(this);
    $this.attr();
    $this.css();
    $this.data();
    $this.click();
    $this.mouseover();
    $this.mouseleave();
    $this.html(name);
}
hello('Hello world');

缩小后的输出是

function hello(b){var a=$(this);a.attr();a.css();a.data();a.click();a.mouseover();a.mouseleave();a.html(b)}hello("Hello world");

这节省了 74 个字节 (37%),几乎是我们节省的字节数的两倍。显然,大型脚本在现实世界中的节省会更少,但您仍然可以通过缓存显着减少脚本的大小。

真的,缓存$(this) 有一个好处。您可以获得微不足道但可衡量的运行时性能提升。更重要的是,您可以减少通过网络传输的字节数,并且直接转化为更多的美元,因为faster page loads equal more sales

当您这样看时,您实际上可以说重复 $(this) 而不缓存它会产生可量化的美元成本

【讨论】:

  • +1,虽然这更能说明为什么你应该缓存 this 而不是 $(this),因为你可以使用 this.value; this.tagName; this.className; this.nodeType; this.... 获得相同的结果
  • @gdoron,使用原始 DOM 和 jQuery 方法有很大的不同;它们并不总是可以互换的。 (addClassdata、动画...)除此之外,var a = $(this); a...; a...;var a = this; $(a)...; $(a)...; 之间仍然存在 3 字节/调用差异
  • 如果您正在对文件进行 gzip 压缩,您通常会发现文件大小会由于此类缓存而稍大一些。在您的示例中,它只有几个字节的差异,111 与 115 字节,但它强调了这一点。我不知道为什么会这样,但我经常发现是这样的。
  • @user1370958,压缩后的文件仍然更小,节省的钱更少。在上面的两个示例中,仅缩小所节省的成本分别为 20% 和 37%; minified + gzipped 节省 7% 和 12%。虽然压缩后的内容可以大于原始内容,但这通常只发生在非常小的文件(
  • 是的,我的意思是,如果您正在 gzip 压缩,那么与使用非缓存 @ 压缩代码版本相比,缓存 this 之类的某些内容可能会导致文件更大987654338@。它们肯定会产生比原始文件更小的文件。重要的不是节省的百分比,因为起点不同,而是需要衡量最终文件的字节大小。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-20
  • 2013-02-11
  • 2010-09-06
  • 2012-10-01
  • 2010-11-15
相关资源
最近更新 更多