What is the cost of '$(this)'?
People here often suggest to cache the jQuery
object created from a DOM
element, like with this code:
$('#container input').each(function() {
$(this).addClass('fooClass');
$(this).attr('data-bar', "bar");
$(this).css('background-color', 'red');
});
- Does caching the jQuery object really improve the performance of our code?
- What happens "behind the scenes" when you pass a DOM element to the jQuery constructor?
In the jQuery tag info this warning appears:
The jQuery function $() is expensive. Calling it repeatedly is extremely inefficient.
Well... that is true only for string selectors, which get parsed with regex to find out what they are:
quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/
Then if the string is a selector (other than id
), jQuery traverses the DOM to find a match with its expensive find
function:
} else if ( !context || context.jquery ) {
return ( context || rootjQuery ).find( selector );
}
So yes it's expensive, but that is only true for selectors!
If we pass a DOMElement
, the only action jQuery does is saving the DOMElement parameter as the context of the newly created jQuery object and setting the length of the context to 1:
// Handle $(DOMElement)
if ( selector.nodeType ) {
this.context = this[0] = selector; // Selector here is a DOMElement
this.length = 1;
return this;
}
I did some tests with jsPerf, and I found that indeed caching the jQuery object has only a little effect:
In Chrome it's only 7% slower. (In IE it's a little bit more significant: 12%.)
To answer your second question, look at the source:
// Handle $(DOMElement)
if ( selector.nodeType ) {
this.context = this[0] = selector;
this.length = 1;
return this;
}
With regard to the performance difference, if you're looking for a direct comparison between the two, it's helpful to remove any extra code that could skew the result, like DOM selection and other methods that aren't directly related.
http://jsperf.com/this-cost/2
In a more real world setting, the relative difference is minor as your test showed
Another thing to keep in mind is that every time you create a jQuery object, memory needs to be allocated for it, which adds to the work that the garbage collector needs to do.
So I think the reason people suggest caching is from somewhat of a principled standpoint. Extra work is being done that, while it usually won't have a noticeable impact, does ultimately require some overhead that can easily be avoided.
One thing that all of the runtime performance tests here miss is another major consideration:
Network bandwidth.
Caching $(this)
into a local variable will generally decrease the size of your script, especially when minified (because this
cannot be reduced from four characters).
Consider:
function hello(text) {
$(this).attr();
$(this).css();
$(this).data();
$(this).click();
$(this).mouseover();
$(this).mouseleave();
$(this).html(text);
}
hello('Hello world');
Closure compiler's minified output is
function hello(a){$(this).attr();$(this).css();$(this).data();$(this).click();$(this).mouseover();$(this).mouseleave();$(this).html(a)}hello("Hello world");
This saves 39 bytes (20%). Now consider:
function hello(name) {
var $this = $(this);
$this.attr();
$this.css();
$this.data();
$this.click();
$this.mouseover();
$this.mouseleave();
$this.html(name);
}
hello('Hello world');
The minified output is
function hello(b){var a=$(this);a.attr();a.css();a.data();a.click();a.mouseover();a.mouseleave();a.html(b)}hello("Hello world");
This saves 74 bytes (37%), nearly doubling our byte savings. Obviously, real world savings in large scripts will be lower, but you still stand to gain significant reductions in the size of your script by caching.
Really, there's only an upside to caching $(this)
. You get miniscule but measurable runtime performance gains. More importantly, you can reduce the number of bytes that travel over the wire, and that directly translates to more dollars because faster page loads equal more sales.
When you look at it that way, you could actually say there's a quantifiable dollar cost to repeating $(this)
and not caching it.