jQuery.fn.each vs. jQuery.fn.quickEach (v66)

Revision 66 of this benchmark created by Andy Harman on


Description

The quickEach method will pass a non-unique jQuery instance to the callback meaning that there will be no need to instantiate a fresh jQuery instance on each iteration. Most of the slow-down inherent in jQuery’s native iterator method (each) is the constant need to have access to jQuery’s methods, and so most developers see constructing multiple instances as no issue… A better approach would be quickEach.

Added bug fix to roviury's version (it was previous faster because it wasn't doing any work).

Added James Padolsey's original quickEach for comparison.

Added "addClass" to body of loops so that JS runtime can't get fancy ideas about optimising the entire body away.

Added quickerEach for comparison.

Added my previous simple while-loops that demonstrate that quickEach should really not be required.

Preparation HTML

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js">
</script>
<script>
  var a = $('<div/>').append(Array(100).join('<a></a>')).find('a');

  //Original version by James Padolsey.
  jQuery.fn.origQuickEach = (function() {
    var jq = jQuery([1]);
    return function(c) {
      var i = -1,
          el, len = this.length;
      try {
        while (++i < len && (el = jq[0] = this[i]) && c.call(jq, i, el) !== false);
      } catch (e) {
        delete jq[0];
        throw e;
      }
      delete jq[0];
      return this;
    };
  }());

  //Updated version supplied by roviury.
  jQuery.fn.updatedQuickEach = (function(jq) {
    return function(c) {
      var i = -1,
          el, len = this.length;
      try {
        while (++i < len && (el = jq[0] = this[i]) && c.call(jq, i, el) !== false);
      } catch (e) {
        throw e;
      } finally {
        delete jq[0];
      }
      return this;
    };
  })(jQuery([1])); //Bug-fix: Previously this was just jQuery().
  jQuery.fn.quickerEach = (function() {
    var jq = jQuery([1]);
    return function(c) {
      var i = -1,
          el;
      try {
        while (el = jq[0] = this[++i] && c.call(jq, i, el) !== false);
      } catch (e) {
        jq[0] = 1;
        throw e;
      }
      jq[0] = 1;
      return this;
    };
  }());
</script>

Test runner

Ready to run.

Testing in
TestOps/sec
.each
//Added "addClass" to give real-world feel.
a.each(function() {
  $(this).addClass("wibble");
});
ready
Original .quickEach
//Implementation by James Padolsey.
//Added "addClass" to give real-world feel.
a.origQuickEach(function() {
  this.addClass("wibble"); // jQuery object
});
ready
Updated .quickEach
//Implementation by roviury (uses finally for exception handling).
//Code is tighter, but the finally bit actually slows everything down
//(especially in Firefox).
//Added "addClass" to give real-world feel.
a.updatedQuickEach(function() {
  this.addClass("wibble"); // jQuery object
});
ready
quickerEach
//Implementation by Andy Harman.
//Added "addClass" to give real-world feel.
a.quickerEach(function() {
  this.addClass("wibble"); // jQuery object
});
ready
while loop
//Could go faster if you don't recreate the jQuery object each run.
var jq = jQuery([1]), i = -1;
while (jq[0] = a[++i]) {
  //Would obviously go faster if you go directly to the DOM instead
  //of using jQuery (performance versus maintenance).
  jq.addClass("wibble");
}
ready
while without body
//While-loop without a body is much faster that while-loop-with-body
//across all browsers that I have tested.
//This seems to be reason for the performance of quickerEach - it
//indicates that an anonymous method can be faster than using a
//while-loop that has a body section.
var jq = jQuery([1]), i = -1;
while (jq[0] = a[++i] && jq.addClass("wibble"));
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.