Introduction
In this tip, I have
tried to capture the common design and coding mistakes JavaScript developers
tend to make. The intended audience for this article is JavaScript beginners with a few years of programming experience.
Background
Over the years, I have
observed how web based Enterprise/Consumer software is designed, developed and
tested. One common trait in all of these applications is the usage of JavaScript. More often than not, developers either overlook or are ignorant about
JavaScript performance. This is partly because these days, the machines are
mostly high end and the browsers with their ever improving script engines have
to some extent camouflaged the performance aspect. So developers are not
penalized for writing bad JavaScript. Nevertheless if developers follow
certain discipline while coding JavaScript, they'll definitely notice that
their app will improve in terms of performance and
maintainability.
Tips on Performance
- Always prefer the Single
var
pattern. JavaScript Engine hosts all the var
s to the top of the function. So
even if we declare our var
at the bottom of the function, it doesn’t matter. Remember
JS isn’t compiled into a binary! In the example below, both functions are the same
but the second one is optimal.
function foo() {
var c = 1;
var d = 0;
var e;
}
function foo() {
var c = 1, d = 0, e;
}
- Do not use
for
-in
loop to
iterate over arrays. Use for
loop instead. for
-in
loops should be used to
iterate user defined object literals. The example below takes more time to execute in comparison with a simple for
loop.
var i, b, c;
for (i in c=[1,2,3]) {
b = c[i];
if(b === "x")
DoSomething();
}
For
-in
loop iterates over every property, even the prototype! So
in the example above, it loops through all properties of c.prototype
and in case
c.prototype
has a property called ‘prop
’, it also iterates over
c.prototype.prop.prototype
, etc. This clearly is not necessary if all we want is to loop through an array.
- Avoid returning undeclared variables from a function.
function foo(a, b) {
r = a + b;
return r;
}
- Cache your objects whenever possible.
for (var i = 0, x = p.length; i<x; i+=1) {
}
for (var i = 0, x; x= p[i]; i += 1) {
}
Even when you use libraries like jQuery, YUI, MooTools, etc., ensure that you cache the selector. Remember DOM Traversal is costly!
function foo() {
var d = $("#myDiv"), c = $("#myDiv:firstchild");
}
- Return
this
from your initializer to facilitate chaining. In the below
example, we are able to call doAjax
on init
because we induced chaining by returning
this
.
var MyApp = {};
MyApp.Utilities = (function () {
return {
init: function (a,b,c) {
return this;
},
doAjax: function() {
}
};
})();
MyApp.Utilities.init().doAjax();
Note: If you have a constructor for your class, you don’t have to return this
explicitly. Constructors always return this
.
- Avoid nesting deep inside a function. Well, this applies to any programming language.
function successCbk(r) {
if (r) {
if (r.data) {
if (r.data.myProp) {
} else {
}
}
}
}
function successCbk(r) {
if (!r) return;
if (!r.data) return;
if (r.data.myProp) {
} else {
}
}
- Avoid memory leaks and circular
references in your closures. Developers typically tend to write the code below:
function foo(e,d) {
$(e).on("click", function() {
});
}
The problem with the code above is that the click callback closure for the element e
keeps a reference to both
e
and d
even though it just uses d
. Similarly, e
also keeps a reference to the closure thus creating a circular reference. These functions will never be
garbage collected. The trick is to break this chain!
function foo(e, d) {
$(e).on("click", cbk(d));
}
function cbk (d) {
}
Parting Note
If you find this tip useful, please share it with the developer community. I haven't executed every line of code in this tip, so in case you find discrepancies,
please comment on the post. I'll incorporate the changes as soon as possible.
Happy learning. :)