5

I ran across some code at work as follows:

var $divs = $('div');
var $jq   = $([1]);

$divs.each(function () {
    $jq.context = $jq[0] = this;
    // ... do stuff ...
});

I perf'd the above, and it seems much faster than having $this = $(this); at the top of the function. Inside the function, the $jq variable has a standard assortment of typical jQuery methods called off of it (.hasClass(), .data(), .find(), ...), and the code works as intended.

I'm wondering what the tradeoffs here are; i.e. what do you lose by not constructing a new object each time? Am I just losing the "history" (i.e. .pushStack(), .end(), .andSelf(), ...)? Should I be doing this all of the time?

Also, is this a named pattern? I couldn't for the life of me figure out how to research this on Google.

Impirator
  • 1,393
  • 1
  • 12
  • 24
  • 1
    Can you link to the perf? – Evan Davis Nov 19 '14 at 21:59
  • ↑↑↑ Ya, not sure you are comparing it correctly as `$this = $(this);` is wrapping `this` inside jQuery object but you don't do it using: `$jq.context = $jq[0] = this;` – A. Wolff Nov 19 '14 at 22:00
  • @Mathletics: Added a link to the jsperf. @A.Wolff—I understand that's _why_ the reused `$jq` is faster, but I'm trying to figure out what the tradeoff is. As in, "Yeah, it's faster, but it no longer supports `end()` correctly, and it kills a kitten." Should I always use the above pattern instead of `$this = $(this)` when possible? – Impirator Nov 19 '14 at 22:13

1 Answers1

1

This strikes me as an insane micro-optimization. In the basic case, sure, it's an order of magnitude faster to reuse the object. But the whole point of caching the current object is to save time in subsequent actions; in the grand scheme it won't matter unless you are processing hundreds of thousands of elements (don't do that.)

What you lose

  1. sane scope: By storing your object in the outer scope of the iteration, you are breaking the closure of the callback. The obvious example is that if you console.log($jq) here, all of the log entries will refer to the same object (the last one in the loop,) versus this which will be unique to each iteration.

  2. readability: var $this = $(this) is a lot faster to read/comprehend than $jq.context = $jq[0] = this.

Regarding "history", I don't see how you'd lose any here. In either case you're starting with a specific object, so any searching you do from there will be recorded just the same.

Evan Davis
  • 35,493
  • 6
  • 50
  • 57
  • Hey Mathletics -- Do you know 'how' that caching works? This is completely new to me, I've never seen its convention: $jq.context = $jq[0] = this; What is going on here? – james emanon Nov 20 '14 at 00:39
  • @jamesemanon `$jq.context = $jq[0] = this` sets the root node and `context` property of the jquery object set up on line 2, `var $jq = $([1])`, result in an object that is essentially the same as `$(this)`. – Evan Davis Nov 20 '14 at 00:56
  • @Mathletics It kind of sounds like reusing the object is perfect for iterating over a collection (as above) when no caching is desired, and it's more of a singleton use-then-throwaway case in the loop body—unless the closure lookup is extremely expensive. Also (point of clarification), `console.log($jq)` only contains the same DOM node after the `.each` loop, not inside. – Impirator Nov 20 '14 at 15:31
  • @Impirator I don't have any idea what you mean that. Re:console.log, all the objects _in the log_ will be the same; it's an issue with the way browsers log objects that change. When the loop is done, all of the log entries will contain the same object. (If you pause and inspect during each iteration, you will see the current object instead.) – Evan Davis Nov 20 '14 at 20:09
  • @Mathletics I guess Chrome has worked that issue out; open the console and plug following in on this page: `var $divs = $('div'); var $jq = $([1]); $divs.each(function () { $jq.context = $jq[0] = this; console.log('object', $jq); });` – Impirator Nov 24 '14 at 16:35
  • @Impirator you need to inspect those objects; they are not what you think they are. Open up one of the objects and you'll see they all refer to `div#svnrev`. (Chrome 38.0.2125.122) The log _descriptions_ are correct, but the actual objects are all the same. – Evan Davis Nov 24 '14 at 18:41