'$(this)' 的成本是多少?

IT技术 javascript jquery performance
2021-02-07 02:24:52

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

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

在 jQuery标签信息中,出现此警告:

jQuery 函数 $() 很昂贵。重复调用它是非常低效的。

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

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

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

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

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

如果我们传递 a 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;
}

用 jsPerf做了一些测试,我发现缓存 jQuery 对象确实只有一点效果:

条形图,如下所述

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

比较不对...性能上有很大的不同。
2021-03-19 02:24:52
在任何一种情况下,您每次都至少保存一个函数调用。
2021-04-13 02:24:52

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

// 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
2021-03-30 02:24:52
现在一个字符串选择器显然会有一个非常不同的图形。
2021-04-06 02:24:52
感谢 SLaks 将我指向 jQuery 源代码。我不知道为什么我自己一开始没有这样做。
2021-04-13 02:24:52

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

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

在此处输入图片说明

在更真实的环境中,相对差异很小,如您的测试所示

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

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

这种比较比接受的答案的比较要好得多。
2021-03-30 02:24:52

这里所有运行时性能测试都遗漏的一件事是另一个主要考虑因素:

网络带宽。

缓存$(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)您获得了微小但可衡量的运行时性能提升。更重要的是,您可以减少通过网络传输的字节数,这直接转化为更多的美元,因为更快的页面加载意味着更多的销售额

当你这样看待它时,你实际上可以说重复而不是缓存它有一个可量化的美元成本$(this)

是的,我的意思是,如果您使用 gzipthis压缩,那么与使用非缓存的this. 它们肯定会产生比原始文件更小的文件。重要的不是保存的百分比,因为起点不同,而是需要测量最终文件字节大小。
2021-03-27 02:24:52
@gdoron,使用原始 DOM 和 jQuery 方法之间存在很大差异;它们并不总是可以互换的。( addClass, data, 动画...) 除此之外,var a = $(this); a...; a...;var a = this; $(a)...; $(a)...;
2021-03-29 02:24:52
+1,虽然它更多的答案,为什么你应该缓存this不是$(this)因为你可以得到相同的结果与this.value; this.tagName; this.className; this.nodeType; this....
2021-03-31 02:24:52
@ user1370958,gzip 压缩的文件仍然更小,节省的更少。在上面的两个例子中,仅仅缩小就可以节省 20% 和 37%;缩小 + 压缩后的节省分别为 7% 和 12%。虽然 gzip 压缩的内容可能比原始内容大,但这通常只发生在非常小的文件(< 50 字节)中。
2021-04-11 02:24:52
如果您对文件进行 gzip 压缩,您经常会发现由于这种缓存,文件大小会稍大一些。在您的示例中,它只有几个字节的差异,111 和 115 字节,但它强调了这一点。我不知道为什么会这样,但我经常发现情况就是这样。
2021-04-13 02:24:52