Better JavaScript

Better JavaScript

As you leverage JavaScript more in creating richer web applications, there are a few practices to be aware of that may help you produce more robust and faster pages. For all of the advantages JavaScript brings, it also has some drawbacks that become apparent the more predominant a part of the page it becomes. The amount of script in a page can easily surpass the amount of HTML. Performance can start to lag.

Reducing Script

With bigger quantities of script being used in the page, the size of the script itself can become an issue. IIS supports compressing responses, but it is not turned on by default. Text compresses very well, which greatly reduces the number of bytes sent to the client. To enable compression in IIS, access the Internet Services Manager plug-in for the Microsoft Management Console. You can do this by entering inetmgr in the run dialog in the start menu. Right-click the Web Sites folder and select Properties. Options for enabling static and dynamic content are on the Services tab. There is CPU cost associated with compression, so you may want to enable it only for static content if the dynamic content is not cacheable (since the static content is always cacheable, it can be compressed once and not repeatedly).

Another technique for reducing the size of script is variable substitution. You will see this referred to as script compression. The approach is to replace variable names with short versions. When writing code, it is nice to have meaningful names, but because the script engine doesn’t care about this, smaller names also work fine. Tools are available that will take script and do this replacement for you. However, it can make debugging difficult, as the variable names and even function names can be replaced with short, meaningless versions. It may seem like reducing the size of the script doesn’t buy you much, and for a single page load, the difference will be small. But when you look at the effect it can have on the server, there is a big benefit. Smaller scripts can be returned faster. The connections and threads used to service the request are freed up sooner, so a single server can scale to accommodate more simultaneous users.

Using Cache Variables

In a compiled language, the code generator can do some tricks to make the executing code faster. A variable referenced repeatedly in a function may have its pointer stored in a register for easy and fast access throughout the function. In JavaScript, variable resolution is late bound. Each time a variable is used, the scope chain is searched to find the object. This can be time consuming. Rather than building up strings while storing them in a DOM element, it’s better to build them up in a local variable and then do a single assignment to the DOM element. When looping through arrays, you can actually improve performance by initializing a variable with the length of the array and checking against that instead of accessing the length property each time.

for(var i=0, var l = someArray.length; i < l; i++) is faster than

for(var i=0; i < someArray.length; i++).

The kind of speedup you might see from this sort of optimization is not nearly as dramatic as what you can get with caching of function pointers. JavaScript function lookups are expensive, and scope resolution and lookups to access functions on DOM elements are even more expensive. One way to think of it is that reducing the number of dots in your code improves the performance. Listing 3-20 has two versions of the same method, inspired by the blog of the Internet Explorer team. While there is nothing technically wrong with the first version, the second will be much faster for any reasonably large collection.

Listing 3-20
Image from book

<script type="text/javascript">
function UpdateValues() {
    var elt = document.getElementById("something");
    for(var i = 0; i < myArray.length; i++) {
        elt.appendChild(myArray[i]);
    }
}

function UpdateValuesCaching() {
    var eltFunc = document.getElementById("something").appendChild;
    for(var i = 0, var l = myArray.length; i < l; i++) {
        eltFunc(myArray[i]);
    }
}
</script> 
Image from book

The second version caches a reference to the appendChild function and then invokes it directly. This saves the overhead of repeatedly resolving the function name to the actual function call. When multiple scopes and namespaces are involved, the performance advantage is increased.