Optimise your JavaScript

Speed up your sites with these tips for slashing the time it takes to execute your code, particularly in older browsers, provided by Joe Angus from We Love

UPDATE: Below is a revision of my article, taking on board the comments and considerations from various readers over the last few days. Rather than retracting this article, I wanted a chance to rectify it with the correct information. I appreciate the time and effort gone into the responses. We live in a digital environment and it’s great that this is possible; it means that we’re always learning.

JavaScript has become essential for all dotcoms wishing to deliver a rich user experience, especially in projects requiring you to avoid Flash. As the power of consumer hardware advances, limitations are vanishing, enabling your sites to perform tasks that only a few years ago would have ground any browser to a halt. Nevertheless, optimising code is still vital for a seamless user experience, so I would like to share some simple rules that can dramatically decrease the time it takes to execute your JavaScript, specifically in older browsers.

Many of these optimisations may not be noticeable if you aren’t working with large sets of data requiring you to iterate a task many times – but even so, I would recommend always adhering to the guidelines below where practical. That way, when you do have to streamline your JavaScript, a large chunk of the work will be done already.

It’s also important to know your target audience, in terms of browser(s) and device(s). For example, in some cases, running jsPerf tests (see below) on my iPhone or iPad gets completely different results to desktop browsers.

Getting started

The very first thing you should do is profile your JavaScript to understand exactly which methods are taking a long time to execute. I would recommend using Firebug’s profiler (if you aren’t aware of what Firebug is, then make sure you go and download it, preferably for Firefox, before continuing to read this article). It’s incredibly easy to use, and you can quickly profile your code without necessarily having any additional work on your part.

There is also jsPerf, which is a fantastic way of creating and sharing test cases, although not as quick as FireBug’s profiler in terms of getting a quick snapsnot.

Firebug is invaluable for profiling your code. Download it from www.getfirebug.com

Firebug is invaluable for profiling your code. Download it from www.getfirebug.com

Accessing the DOM

It’s quite likely that your JavaScript will be accessing the DOM, and actually it’s more than likely the cause of any performance issues. It’s very important to understand that this is an expensive task, due to the fact that the browser has to re-render the screen every time the DOM has changed. I would first try removing your DOM calls to gauge just how expensive they’re being before tackling the smaller improvements below.

Where possible, it’s quicker to manipulate the DOM in one large hit, rather than many little ones, reducing the amount of times the browser has to update. Instead of appending many elements directly, try injecting them into a document fragment, and then appending the fragment into your document (read more about document fragments).

The same can be said about applying inline styles directly onto elements. Instead, add and remove classes to minimise browser reflow time.

Construct code to perform any changes to the DOM in one go, to minimise the number of times the screen is redrawn

Construct code to perform any changes to the DOM in one go, to minimise the number of times the screen is redrawn

Building strings

Older versions of Internet Explorer (versions 5-7: 8 does not display the same problem) get slower and slower the longer your string is. We'll fix this below.

Note that in newer browsers, you won’t see any improvement. In fact, in many cases you will see the opposite – all the more reason to profile your code to see if the benefits in older browsers outweigh the impact on newer alternatives, and decide which your target audience would most benefit from.

If your strings are short, stick to the first method shown below.

Fastest method in most cases:

var string = 'abc'; string += 'def'; string += 'ghi'; string += 'jkl'; string += 'mno';alert(string);

Try doing this for old browsers struggling to concatenate large strings quickly:

var string = ['abc...', 'def...', 'ghi...', 'jkl...', 'mno...'];alert(string.join(""));

Speeding up them loops

By simply rearranging for loops, you can squeeze a little more performance out of your code.

Change from:

for(var i = 0; i < my_array.length; i++){ }

Or:

for(var i = 0; i < myMethod(); i++){ }

Instead, try this subtle change:

for (var i = 0, len = my_array.length; i < len; i++) { }

Or:

var len = myMethod();for(var i = 0; i < len; i++){ }

Raymond Julin has kindly has put up a jsPerf test case so you can see the difference yourself. In literally every browser, you can see a nice performance gain.

Ensure that there are no unnecessary function calls within your loops. If you can do it once, outside of the loop, it’s usually a good idea to do so. The performance gain will depend upon how expensive the function is.

Avoid:

for(var i = 0, len = my_array.length; i < len; i++){ var response = goDoSomething();}

If it’s appropriate, try:

var response = goDoSomething();for(var i = 0, len = my_array.length; i < len; i++){ }

Another optimisation to consider, when you aren’t reliant on the order of the loop, is to decrement instead:

var i = my_array.length; while( i-- ) { }

I would use this last method sparingly: it’s not the most logical way to loop and could make it more difficult for other programmers needing to understand your code, but it could still be useful in the right scenario.

Calling methods or properties on array elements

If you’re accessing properties or methods on elements within an array multiple times, it’s faster to declare a variable holding that element. It can mean you need extra lines of code, but if it's a regular occurrence, the resulting performance gain can make it worthwhile.

For example:

myArray[myIndex].myMethod1();myArray[myIndex].myMethod2();myArray[myIndex].my_variable;

...is slower than:

var element = myArray[myIndex];element.myMethod1();element.myMethod2();element.my_variable;

Again, Raymond has a jsPerf test case for this one where you can see the performance gains across browsers.

This is also true for any lookups, not just arrays. The more you can avoid doing multiple lookups, the better.

The scope chain determines the order of operations JavaScript performs when finding a referenced variable

The scope chain determines the order of operations JavaScript performs when finding a referenced variable

Local versus Global variables

Whenever you reference a variable, JavaScript will look through the scope chain in order to find it. First, it will look through the local variables within the current scope, and then work its way up through the chain to the globals. Therefore, accessing global variables is far slower than accessing local ones, with varying effects depending upon the level of nested scope.

For example:

var my_var = 1;MyClass = function(){ var my_var = 1;}

Within the scope of an object of the above class, my_var is its local variable and the first place within the scope chain JavaScript will look at. This is quicker than if the local declaration didn’t exist, and it had to move up the chain to find the global variable.

Sometimes it’s possible that you will need access to a global value within a different scope, especially in loops, in which case it’s more efficient to bring that variable into the local scope purely to access the local version within the iterations.

var global_var = 20;function MyFunc(){ var local_var = global_var; for(var i = 0, length = 999; i <= length; i++){ // Now you can access the local version for quicker access times. }}

It’s also worth remembering that if you don’t put ‘var’ in front of your declarations, it will automatically be created as a global variable. For that reason alone, it’s good practice always to declare the scope of variables. This can also help if you’re debugging a scope problem.

Conclusion

Some of the techniques above will give you far less noticeable performance gains than others, and there are many more techniques which I haven't covered (for example, bit shifting). However, these are the methods I’ve found most effective for getting the most performance out of my JavaScript in older browsers. Remember to look into your DOM calls first, as they’re far more expensive and can improve performance far more noticeably than the other methods.

Please do add your comments below to let me know your opinions and how you’ve managed to work around potential performance-related problems in your own JavaScript.