Tuesday, October 12, 2010

Understanding JavaScript Closures

This week, when I was studying other JavaScript libraries (prototype, JQuery), I noticed that closure is frequently used in the code. Then I decided to do some research on JavaScript closures. In this blog post, I would like to share something I learned about JavaScript closures.

1. What is JavaScript closure?

There are plenty definitions for JavaScript closure. It seems that every one has his own understanding towards it. In my understanding, closure is an internal reference that keeps the value of all the local variables that used inside a function even if the function has returned. It is intangible and always existing when a function is called within another function. Therefore, the key feature for JavaScript closure is that it keeps the value of the local variables within a certain scope(For example, a function).
Then you might say, an object can keeps the value of the variables well. What is the difference? The difference is that the variable in object can be accessed from outside where the variable in closure is not accessible from outside. For example:
function Person(name) {
this.message = name + " says hi!";
};
Person.prototype.greet = function greet() {
alert(this.message);
};

var Yichi = new Person('Yichi');
Yichi.greet();
In traditional object manner, people can modify the "message" variable by saying "Yichi.message = "somthing else";"
function Person(name) {
var message = name + " says hi!";
function greet() {
alert(message);
}
return greet;
};

var Yichi = Person('Yichi')();
When using closure, variable "message" is completely private.

2. When use JavaScript closure?

Since JavaScript closure keeps the local variables alive in a function, it is very useful for those functions that are called asynchronously. For example, AJAX callbacks, which I definitely going to use in my library, they only get called when they receive the response from the server. Another example is event handler, it is called only when certain event is triggered. The simplest example shows the power of closure would be setTimeOut function. Here is the traditional way without closure:
function handle() {
alert(message);
}

function setAlarm(message, timeout) {
setTimeout(handle, timeout);
}

setAlarm("Hello World!", 100);
In this case, variable "message" is undefined, because when handle() is called, the value of variable "message" is no longer kept in the memory. It will only work if you pass "message" as a parameter when calling handle(). Let's see how closure does:

function setAlarm(message, timeout) {
function handle() {
alert(message);
}
setTimeout(handle, timeout);
}

setAlarm("Hello World!", 100);
This time, the system will alert "Hello World!" after 100 milliseconds. It is because closure keeps the value of "message" in the scope of "setAlarm" function, and since "handle" is called within this scope, so it knows the value of "message". From this example, we can tell that using closure will save your work in a smart way.

3. Be careful with JavaScript closure

Since other programming languages, such as C or Java, do not have any feature like JavaScript closure, new JavaScript developers are not familiar with this concept. Therefore, extra caution is really necessary when using closure in the code. One common bug producer is defining function within a loop. Let's focus on the following example:
function main(link) {
for (var i = 0; i < list.length; i++) {
link[i].onClick = function() {
alert(i + 1);
}
}
}

main(document.getElementsByTagName("a"));
It looks like it will alert an index number when click on a link. However, this piece of code is not going to work that way. Assume there are 3 links in the page. Then the list length is 3 in the code. In this case, 'i' will be 4 when loop ends. Since closure keeps all the local variables within the parent function, it will always alert '5' when link.onClick is triggered. To fix this bug, it has to be like this:
function main(link) {
for (var i = 0; i < list.length; i++) {
link[i].onClick = (function(j) {
return function() {alert(j + 1);}
})(i);
}
}

main(document.getElementsByTagName("a"));
In the above example, another anonymous function is defined, which creates a new closure that only stores the local variable "j". Because "j" stores a snapshot of "i", so the system will alert different numbers (1,2,3 in this case).
This example shows that we must fully understand closure and use it correctly, otherwise, weird bugs might raise.

No comments:

Post a Comment