Introduction
Continuing from where I left last time, let's delve a bit deeper into performance aspects of JavaScript. While Part 1 was about basics, this section explores certain memory management techniques in JavaScript. So before you proceed, please refresh your memory on prototype and its internals.
Background
This tip assumes some basic knowledge of prototype and some internals on how functions work in JavaScript. Readers can read this tip independent of Part 1.
Tips on Performance
- The problem with functions inside functions:
function Person(fname,lname) {
this.firstName = fname;
this.lastName = lname;
this.getName = function(){
return this.firstName + ' ' + this.lastName;
}
}
var p1 = new Person("Jack", "Smith");
var p2 = new Person("John", "Doe");
Everytime you create a new person, JS Engine allocates memory for firstName, lastName
and getName
. I don't mean to say that this is wrong but this is inefficient. Isn't it? So, what's the solution? Move getName
to prototype!
- Move common functions to prototype:
Person.prototype = {
getName : function(){
return this.firstName + ' ' + this.lastName;
}
}
Once we move the getName
method to prototype, you will observe that p1
and p2
will just hold a reference to the single memory block allocated for getName
. How will this help? Well, if your app is creating many Person objects on the fly, less memory is allocated by browser when creating new Persons and hence the Garbage collector which kicks in intermittently in the app life cycle will quickly do its job and move on improving your app's user experience by reducing the flicker.
- Array memory management:
function foo() {
var arr = [];
....
arr.push(1);
....
arr = [];
arr.push(2);
....
}
What's wrong with the above code? The function foo
simply creates an array, pushes an item and then somewhere down the line cleans up the array and pushes another item. Well, first looks can be deceiving at times! Observe closely that second time, the array is recreated. So effectively, JS Engine requests for another memory block and the browser allocates one. The older memory block is awaiting garbage collection. Hmm... is it necessary to recreate an array? Well, the intention was to just clean up not allocate a new array. So, what's the solution?
arr.length = 0;
Observe that if you replace the above statement with the cleanup statement in foo
, you still get the same result! The difference is that no new memory block is allocated by the browser in this case.
- Keep
private
methods outside the plugin's return
.
(function($){
$.fn.pluginName = function(config){
return this.each(function(){
function doSomething(){};
});
}
})(jQuery);
$(".myClass").pluginName({o:1});
The problem with the above jQuery plugin is that the function doSomeThing
is created for each iteration of the selector. So if your jQuery selector returns 10 DOM elements which have a CSS class called 'myClass'
, your plugin is consuming lot of memory by requesting the engine to allocate 10 blocks for doSomething
. How can we avoid this? Well, simply move your private
function outside of this.each
iteration!
(function($){
$.fn.pluginName = function(config){
return this.each(function(){
myPrivates.doSomething();
});
var myPrivates = {
doSomething : function(){};
};
}
})(jQuery);
$(".myClass").pluginName({o:1});
You don't necessarily have to create a private
object, just moving out doSomething
out of the iterator is good enough!
- Avoid anonymous functions inside
setTimeout
or setInterval
:
setTimeout(function(){
}, 2000);
What's wrong with the above code? You should have guessed it by now if you got the theme of this article. Yes, the JS Engine simply creates a new anonymous function every 2 seconds! More garbage and you know the consequence! The solution is obviously to use a named function and reference it inside setTimeout
.
function myCallback(){
};
setTimeout(myCallback, 2000);
- See if you can get away by using
Static
classes in your app. Use instance classes only when necessary. The idea is to use memory sparingly - only when it's an absolute necessity.
var MyApp = MyApp || {};
MyApp = (function () {
var doSomething = function() { }
return {
init: function (a) {
};
};
})();
MyApp.init();
Parting Note
The improvements listed above are common pitfalls which can be avoided in our day to day coding. I hope you find these tips useful. If yes, please share it with the developer community.
Happy learning.