Thursday, March 22, 2012

High Performance Webapps. jQuery Objects as Arrays.

After recently participation in a workshop Real Life JS: High Performance Webapps. I have decided to share some tricks to achieve better perfomance for websites. There are 100+ tricks. It's impossible to covers all of them. In this post I would like to show a difference between using of jQuery calls and JavaScript native ones. Especially using of $.each() method for iteration is the topic of this post. I already knew that using of $.each() is slow. This fact is described in 15 Powerful jQuery Tips and Tricks for Developers (chapter 3). What is an alternative to $.each()? How to iterate faster on result set of running a selector? The result of running a selector is an jQuery object. However, the library makes it appear as if you are working with an array by defining index elements and a length. Use a native JavaScript for or while loop on the result. Check this example please. Perfomance benefit is huge in my opinion. I also learned that any method call in JavaScript brings a lot of overhead. I also learned that literal notation like this one
var myArray = [];
var newElement = "test element";
myArray[myArray.length] = newElement;
always works faster than
var myArray = new Array();
var newElement = "test element";
myArray.push(newElement);
Never call new operator if you can use literal notations for arrays and objects in JavaScript. Never call methods like push() if you can do this in another way. Difference in execution time of the code parts above is significant in loops. The main rule here: "Less operations per iteration - less execution time. Doing nothing is the best from performance point of view". An example of bad programming would be
for(var i=0; i < myArray.length; i++) {
    ...
}
because length is not cached and myArray.length is executed for each iteration step.

Today, I've played a little bit with the new great feature in PrimeFaces jQuery Selector API meets JSF. My aim was to compare $.each(), array's push() and $.attr() calls with native JavaScript calls. I prepared a small test page with a simple selector and the code from PrimeFaces core.js. Alternative I rewritten the code in PrimeFaces with native calls to show the difference. First, I ended up with this code. The main code is here:
var selector = "@(ul li)";
var jqSelector = selector.substring(2, selector.length - 1);
var components = $(jqSelector);
var ids = [];

console.time('Native Loop');
var length = components.length;
for(var i=0; i < length; i++) {
    ids[length] = $(components[i]).attr('id');
}
console.timeEnd('Native Loop');

console.time('jQuery Each');
components.each(function() {
    ids.push($(this).attr('id'));
});
console.timeEnd('jQuery Each');
My Firefox 11 shown (average time):

Native Loop : 28 ms
jQuery Each : 37 ms

IE8 shown:

Native Loop : 249 ms
jQuery Each : 281 ms

After that I deleted $ around components[i] in the for loop and replaced $.attr('id') by native .getAttribute('id') (what I always use in my not jQuery / JSF apps). Changes:
var length = components.length;
for(var i=0; i < length; i++) {
    ids[length] = components[i].getAttribute('id');
}
Look this test page please. Now we have in Firefox 11:

Native Loop : 3 ms
jQuery Each : 42 ms

and in IE8:

Native Loop : 14 ms
jQuery Each : 274 ms

Perfomance benefit in IE8 (with choosen selector) is 260 ms. Not bad. This trick can give more performance optimization in a huge page. I'm not a game developer and don't develop critical web applications. But a performance optimization is always good for me anyway. Optimization means not only CSS and JavaScript optimization. Loading of images / icons can be optimized as well. Next post(s) will cover more such stuff.

2 comments:

  1. Nice!!!
    Going to think twice next time I'll do similar things...
    Thanks for the tips!

    ReplyDelete
  2. You've made some great points and I don't think I could have made them any better.
    Thanks for the good information you have shared here about making more optimization on the site !

    ReplyDelete