Wednesday, July 17, 2013

Fine Tune JavaScript - Improve Performance with Local Variables

We all know literal values and local variables can be accessed very quickly, whereas array items and object members take longer. But what is a reason behind it? In this article I will talk about how you can improve performance of your existing code with the use of local variables wisely.
Let's start with a simple example -

function updateClass(){
    var divList = document.getElementsByTagName("div");
    for(var counter = 0; counter<divList.length; counter++){
        divList[counter].className = document.body.className;  
    }
}

Before improving the performance of the function let's see what all artifacts/internal objects are created when the function created and executed. When the function is created, it's [[Scope]] property is initialized with a collection of global variables. More info about Scope Chain is available in the earlier post - JavaScript Jargon - Scope Chain, Execution Context and Activation Object
Some of the global variables which are available in the [[Scope]] object are -
  • this
  • window
  • document
  • updateClass
When the function is executed, a new internal object - Execution Context, is created which has it's own scope chain. This scope chain is initialized with the objects present in the scope chain which was created when the function itself was created. Once the initialization is over, another internal object - Activation object, is created and pushed on top of the execution object's scope chain. The activation object acts as the variable object for this execution and contains entries for all local variables, named arguments, the arguments collection, and this. In this case activation object will have -
  • this
  • divList
  • counter
  • arguments[] - it null in this case as there are no arguments passed to the function
The scope chain of the execution context object now will have two entries - Activation Object and Global Object.

Each time a variable is encountered during the function’s execution, the process of Identifier Resolution takes place to determine where to retrieve or store the data. It first starts with searching the variable in the execution object's scope chain and goes all the way down to the global object, if it is not found in the first place. It is this search process that affects performance. The deeper into the execution context’s scope chain an identifier exists, the slower it is to access for both reads and writes. Since local variables are always available in the activation object, therefor their access is always fastest as compared to the global variable which are available in the end of the chain.
A good rule of thumb is to always store out-of-scope values in local variables if they are used more than once within a function. In the function above, the document object is being accessed multiple time, Since the document object is a global variable, therefore every time it is being accessed you have to pay the performance penalty. Based on this information here is the improved version of the function -

function improvedUpdateClass(){
    var docObject = document;
    var divList = docObject.getElementsByTagName("div");
    var className = docObject.body.className;
    var divCount = divList.length;
  
    for(var counter = 0; counter<divCount; counter++){
        divList[counter].className = className;  
    }
}
In this improved version, instead of accessing the document object  multiple times, it's reference is being stored in a local variable - docObject, which is being accessed in rest of the function. Also we have created a local variable for storing body's class name - className, which is again improves performance.

No comments: