In programming, encapsulation means combining data with the methods that operate on that data. It's an integral part of object oriented programming, and commonly used in modern JavaScript. For example, say you create a Carousel-style widget. You might write one Carousel object that tracks the pictures it's using and provides methods to get and set those picture, or cycle through them programmatically. This is good, because it allows JavaScript to be modular, reducing dependencies and namespace clutter. Encapsulation is our friend, but excessive nesting can cause unnecessary closures which negatively affect your JavaScript performance.
In the following example, inner exists only within the scope of the function main. Every time you call main() a new inner is created, and every time main finishes running, that inner is destroyed.
function main() {
function inner(str) {
return { name: str };
}
return inner('myName');
}
Contrast the above with the following code, where there is only ever one outer, no matter how many times you call main():
function outer(str) {
return { name: str };
}
function main() {
return outer('myName');
}
The difference is small in the above example, because the functions are so simple. But consider the following (admittedly contrived) comparison:
// shared variables for performance testing
var i, time, numLoops = 100000;
// with nested functions
function inners() {
function nestOne(str) { return str + ' + nestOne'; }
function nestTwo(str) { return str + ' + nestTwo'; }
function nestThree(str) { return str + ' + nestThree'; }
function nestFour(str) { return str + ' + nestFour'; }
function nestFive(str) { return str + ' + nestFive'; }
var data = '';
time = (+new Date); // start timing
for (i = 0; i < numLoops; i++) {
// call everything
data = nestOne(nestTwo(nestThree(nestFour(nestFive('testing')))));
}
time = (+new Date) - time; // stop timing
alert('inners() took ' + time + ' milliseconds');
}
// no nested functions
function one(str) { return str + ' one'; }
function two(str) { return str + ' two'; }
function three(str) { return str + ' three'; }
function four(str) { return str + ' four'; }
function five(str) { return str + ' five'; }
function outers() {
var data = '';
time = (+new Date); // start timing
for (i = 0; i < numLoops; i++) {
// call everything
data = one(two(three(four(five('testing')))));
}
time = (+new Date) - time; // stop timing
alert('outers() took ' + time + ' milliseconds');
}
// run tests
inners();
outers();
When I run the above tests in Firefox 3.6 outers() is about 30% faster than inners(), and in Internet Explorer 8, outers() is about 20% faster initially. The difference is staggering when you consider that the functions barely do anything! Imagine an actual web app, where you might be doing intense calculations, manipulating DOM elements, or performing other tasks that take real time and memory. Chrome takes about the same amount of time in each case, but that's because it has internal processes that modify your scripts before running them to improve performance.
It's important to note that there is not only a performance difference here, there is a scope difference. When you pull nested functions out of their parent function, they now become available outside of their original scope. Depending on the structure of your application this may cause other side effects. Take a look at the big picture and determine if the nested functions really do need to be nested, but understand that they might then be recreated many times over the life of your app. In some cases if the parent function runs for a long time, there could be several copies of each inner function running simultaneously, causing potential memory issues.
I'm a Front-End Engineer at Yahoo! working on the Mail and Messenger teams. I blog about web design and development topics including accessibility, usability, performance, and developing HTML / CSS / JavaScript applications on Appcelerator Titanium and Adobe AIR.
If you're a web developer, you might enjoy Jelo, my JavaScript library.
All original work on this site is covered by a Creative Commons Attribution 3.0 license unless otherwise specified.
You may share or use any code or images from this site in any manner, for free, so long as reasonable effort has been made to give credit where due.
The views expressed in the posts and comments on this blog do not necessarily reflect the views of Yahoo!