Freigeben über


Performance Tip #1: Variables in the DOM

In this blog I’m going to talk about a simple rule for speeding up JavaScript. Plenty of other folk have commented on this but it’s worth saying again and again :).

Advice in a nutshell: Try to declare all your functionality within closures and avoid declaring variables in the global scope or any part on the DOM.

Background

When you declare a variable in JavaScript that’s in the global scope it is defined onto the window object e.g.:

 <script type="text/javascript">
    var foo = 1;
</script>

Variable foo can be accessed from anywhere on your page as either foo or window.foo. That sounds great as it’s a way to share data across functions and scripts. While it certainly does that it does it at a cost.

Because it’s defined on window that means that the variable is actually now part of the DOM. Which doesn’t sound too bad, except that the DOM isn’t actually part of the JavaScript engine  in IE. Which might not make sense at first but look at this way, JavaScript is only one of the ways you can manipulate the DOM. The other obvious way is the HTML on the page itself, but more subtlety other script engines can manipulate the DOM. For example in IE you can have JavaScript and  VBScript on the same page. So now two different script engine can access the DOM and the variable window.foo.

This is where it starts to get complicated as a script engine can’t own DOM elements then they need to be brokered through a 3rd party i.e. the host, IE (more specifically the IActiveScript host) . Now whenever you need to look up window.foo the JavaScript engine needs to ask the host if the host has that variable. Also doesn’t sound too bad, except the most logically way of doing this one in Windows is via a mechanism called COM. Now many books have been written on COM but if I was given a sentence to describe it I would say. COM is a technology that enables different components to communicate with each other either in process, out of process or over a network. As the DOM and the JavaScript engine are different components it sounds like a great match and it is. But of course the mechanics of making two separate components work isn’t so simple. It requires dynamic integration of objects and type marshalling all of which cost CPU cycles.

Comparison

Comparing the two different scripts below, the first reads the variable via the DOM 1 million times the 2nd reads a local variable 1 million times.

Script 1
 <script type="text/javascript">
    var dateStart = new Date();
    var someMessage = "yay a message";
    for (var i = 0; i < 1000000; i++) {
        var x = someMessage;
    }
    var elapsedTime = new Date() - dateStart;
    alert(elapsedTime);
</script>

Script 2
 <script type="text/javascript">
(function () {
    var dateStart = new Date();
    var someMessage = "yay a message";
    for (var i = 0; i < 1000000; i++) {
        var x = someMessage;
    }
    var elapsedTime = new Date() - dateStart;
    alert(elapsedTime);
})();
</script>

Executing the test 10 times and then taking an average I got the following:

 

Script 1 (DOM)

Script 2 (closure)

Difference

IE8

343ms

42ms

810%

Chrome 4

85ms

3ms

2,833%

Firefox 3.6

3ms

3ms

0%

As you can see accessing variables via the DOM is a lot slower than accessing them locally, for me it was 8x slower. While a million access might sound like a lot it’s not actually that many, try using the IE8 profiler on some sites as you type an email or chat online and you might be surprised as to how much goes on, a topic for another day.

Bonus Tip: Timings in IE & Date

In IE8 and below the clock used by Date has a resolution of ~10ms but if you go to the developer tools and press start profiling we change the clock resolution to be 1ms. This has some other side affects but nothing to worry about.