Programming Guidelines

Modified on 2010/05/08 01:18 by Stephen Walther — Categorized as: Uncategorized

The topics in this section are intended to prevent developers from making common mistakes, or to highlight practices that can have a tangible effect upon performance, maintainability, or supportability.

5.1 Variable declaration

All variables should be declared using the “var” keyword to minimize the overheads in traversing the scope chain. Do not use global variables unless absolutely necessary as they are always the slowest to lookup. Variables should be initialized to a default value or explicitly set to null. For example:

    var counter = 0;
    var empty = null;

It is preferred to declare variables together at the top of a function. Group variables to minimize script size when possible, for example;

    var counter = 0, empty = null;

JavaScript does not have a notion of block scope; therefore variables defined within "if" or "for" blocks inside a function are accessible from anywhere within that function. This means it is not perceived a benefit to delay variable declaration until they are to be used.

For example:

function doSomething() {
    var counter = 0, resultArray = [];

// code removed }

Avoid using the “with” statement. It reduces the clarity of code and modifies the scope chain, introducing performance penalties.

5.2 Function Shortcut Use

Avoid using function shortcuts such as $get within script that must coexist on a page with other scripts, or may do in the future. This is to avoid other scripts reassigning a global shortcut to an alternative function, which may cause your script to fail.

Instead use fully qualified function calls whenever a script may be reused in uncertain or shared circumstances, or for non-global functions use a closure as described in section 3.7.1.

5.3 Function Aliasing

When calling a function many times, such as in a loop, alias the function to reduce scope chain traversals. For example;

function doSomething() {
    var get = getDataByIndex;
    for (var counter = 0; counter < 10000; counter++) {
        var current = get(counter);
        // ...code removed
   }
}

Keep in mind that a function must be designed to be used in this way; for example, functions that rely on "this" may be unsuccessfully aliased.

5.4 Comparisons and Equivalence

Wherever possible use the strict equality operators to determine whether two values are equal. This prevents inefficient implicit type conversions and ensures that the responsibility for precision relies with the programmer. For example:

If “x” and “y” both have numeric values;

Wrong: if (x == 5 && y != 4)
Right: if (x === 5 && y !== 4)

5.5 Undefined and Null

Do not switch between using the undefined and null keywords. Ensure that all fields and variables are initialized to a value or null:

    this._result = null;

Failing to do this would mean that _result would be undefined. When testing that a class instance variable is not set, or whether an optional parameter has been provided to a function, and the variable or parameter is known to be an Object, use the following syntax:

    if (message) {
        // message was provided and not null
    }

These two keywords are not equivalent, yet this approach reduces the occurrences of failed comparisons.

Finally, when explicitly testing whether or not a variable has been declared (such as checking whether a pageLoad function is defined, for example) use the following syntax:

    if (typeof(pageLoad) === 'undefined') {
        // pageLoad has not been defined
    }

This is a useful technique when it is necessary to detect the difference between undefined, null, false, zero, and empty string rather than treating all as equivalent.

5.6 Loops and Recursion

Loops and recursion can exaggerate the negative impact of poorly performing code. Therefore they should be a focus for good practices from the start, and optimized when deemed necessary. Some good practices are:


Wrong:

for (var counter = 0; counter < 10000; counter++) {
	    try {
	        performAction();
	    } catch (e) {
	        alert('Failure: ' + e);
	        break;
	    }
	}

Right:

try {
    for (var counter = 0; counter < 10000; counter++) {
        performAction();
	    }
} catch (e) {
	    alert('Failure: ' + e);
	}


Wrong:

for (var counter = 0; 
     counter < document.getElementsByTagName('div').length; 
     counter++) {
    performAction();
	}

Right:

var target = document.getElementsByTagName('div').length;
for (var counter = 0; counter < target; counter++) {
    performAction();
	}

In this example, the call to getElementsByTagName would be recalculated from the DOM for every iteration. This is unlikely to be the intended behavior. Also ensure that performAction does not modify the loop collection.


Wrong:

for (var counter = 0; counter < 10; counter++) {
    performAction();
	};
for (var counter = 0; counter < 10; counter++) {
    performOtherAction();
	};

Right:

var counter;
for (counter = 0; counter < 10; counter++) {
    performAction();
	};
for (counter = 0; counter < 10; counter++) {
    performOtherAction();
	};

5.7 DOM Manipulation

5.7.1 Batch Up Changes

Manipulating the Document Object Model from JavaScript is expensive, and can result in requiring the browser to reflow the document. To reduce the number of DOM tree updates and screen repaints batch up changes and apply them at once.

Avoid concatenating HTML to the innerHTML property of elements (for example by using the += operator) as this potentially requires a large string concatenation before the DOM is modified.

5.7.2 Circular References

Circular references between DOM elements and JavaScript objects can lead to memory leaks in some older browsers. Although this issue is reduced in significance now, it should be considered for internet applications. Avoid storing references to JavaScript objects on expando properties on DOM objects as this reduces the likelihood of introducing memory leaks.

To understand more about memory leak patterns see “Understanding and Solving Internet Explorer Leak Patterns” (http://msdn.microsoft.com/en-us/library/bb250448(VS.85).aspx).

5.8 Use of eval

The “eval” keyword invokes the JavaScript compiler and is therefore very slow. Avoid using eval where possible. Never use eval or the Function constructor on un-trusted data as this creates a potential script vulnerability. If eval is essential, use window.eval to execute in the global scope rather than just eval. If using the ASP.NET Ajax Minifier ensure that you understand the "evals as safe" setting. Other minifiers may have equivalent settings.