9

I started a series of posts on javascript / jQuery optimization and stumbled upon this interesting result.

Why is it that minimizing jQuery objects (by searching from a cached jQuery collection) can be slower then creating more instances of jQuery objects?

I was stunned to see the results of a test i prepared. I always thought that minimizing creating $ instances are slower.

This is what i am used to write, as i cache the parent (i call it "appRoot").

var appRoot = $("#appRoot");
    appRoot.find(".element1").css("color","red");
    appRoot.find(".element2").css("color","blue");

vs

    $(".element1").css("color","red");
    $(".element2").css("color","blue");

See the test results (slightly different scenario). jsperf minimize-jquery-object-creation it turns out that the cached snippet is slower then the uncached snippet.

I am trying to understand why?

adardesign
  • 33,973
  • 15
  • 62
  • 84

3 Answers3

3

I think the find() call is what's slowing things down.

The only reason to cache a jQuery object is if you're going to be referencing or manipulating it multiple times. If you're just setting a CSS property and that property's not going to be changed by for the life of the rendered page, then there's no reason to define a cache variable.

Adrian J. Moreno
  • 14,350
  • 1
  • 37
  • 44
  • Interesting point, we will need to change the test, any idea how to get down to the bottom of this? – adardesign Jul 12 '11 at 20:30
  • It appears this is correct. `.find()` filters through the elements checking to see if the children matching the selector while `$('.className')` uses the native `document.getElementsByClassName` if it exists. – scurker Jul 12 '11 at 20:35
  • so `$(".element1",appRoot)` will be fastest? – adardesign Jul 12 '11 at 20:37
  • @adardesign It appears so, yes. If sizzle finds a class and a context is passed, `context.getElementsByClassName` is used. – scurker Jul 12 '11 at 20:39
  • 1
    @adardesign: `$(".element1",appRoot)` is ultimately identical to `appRoot.find(".element1");` – user113716 Jul 12 '11 at 20:41
  • See http://jsperf.com/minimize-jquery-object-creation/11 which has a little different results, complicating this a little more :) – adardesign Jul 12 '11 at 20:42
  • ...[here it is in the source](https://github.com/jquery/jquery/blob/1.6.2/src/core.js#L170-174). It just gets flipped around to use `.find()`. – user113716 Jul 12 '11 at 20:44
2

I think it is because the in "more jquery objects created", jQuery can make a direct use of the recent API

document.getElementsByClassName("classvalue")

in the other case with "less jquery" you have to always verify that the element found are under #appRoot which takes more time.

Here is another test using document as appRoot which seems to close the gap a little on the second run: http://jsperf.com/minimize-jquery-object-creation/6

Jerome WAGNER
  • 21,986
  • 8
  • 62
  • 77
  • 1
    I wondered the same thing, except that it could use appRoot.getElementsByClassName("element1") to still use that native function, but just return children of appRoot. – jfriend00 Jul 12 '11 at 20:38
2

You need to consider that your test contains less than 10 divs or other html elements. The reason to write code like in the first example is to make the selector faster, but with the cost of additional method calls. Normaly the selector should be the more expensive of the two by far so the the gain would outweight the loss but with a DOM this small the selector will be very cheap no matter how you write it.

People often make the misstake of not remembering that a more complex and large DOM will change the bottlenecks of the code. I think jsperf should have some kind of warning about this.

Mikael Eliasson
  • 5,157
  • 23
  • 27