If you’re wondering what a closure is, or how to use one, you should first understand scope. As an example, whenever you call a function, you expect it’s variables to die once the function ends. All it’s variables are limited in scope to the function, which is how you normally want it. To illustrate, let’s start with a typical javascript version of a class:
<script type="text/javascript">
function anObject() {
this.publicVar = 'Accessible!'; // A public variable
var privateVar = 'Not directly accessible...'; // A private variable
var privateMethod = function() {
// A private method
alert('Can\'t call me directly either!');
}
this.publicMethod = function(param) {
// A public method
this.publicVar += param;
}
this.accessPrivate = function() {
// A privileged method
alert(privateVar);
privateMethod();
}
}
newInstance = new anObject();
alert (newInstance.publicVar);
newInstance.publicMethod(' Modified by publicMethod.'); // Let's change the public var
alert (newInstance.publicVar); // Test to see if publicVar changed...
alert(newInstance.privateVar); // Undefined, You can't show private variables!
// Let's see if we can get at the privateMethod
try {
newInstance.privateMethod();
}
catch(err) {
alert("You can't get at the privateMethod directly. Let's try another way");
}
// Now try with a privileged method
newInstance.accessPrivate();
</script>
Now here’s a closure. At first brush, it seems to operate like a class but it’s quite different. Here you have a single object “aCat” that’s equal to a function. The (); following the function, tells javascript, to execute the function the second it’s parsed. So in a nutshell, “aCat” will equal whatever the function returns, and in this case it’s a collection of functions. Here, what’s returned is what creates the closure. Those functions are granting you access to the variables that have been closed over when the parent function ended and returned.
<script type="text/javascript">
var aCat = function() {
// A bunch of private variables and functions
var miceCaught = 1;
var currentActivity = 'sleeping';
var changeActivity = function(action) {
alert('I was ' + currentActivity +", now I'm" + action);
}
return {
/**
* Here we are creating the closure.
* Returning methods to access the
* closed private variables.
**/
showMiceCaught : function () {
alert(miceCaught);
},
catchMouse : function() {
miceCaught++;
},
changeActivity : function(action) {
changeActivity(action);
}
}
}(); // Here we tell javascript to run the function immediately
// We can now access the closed over variables and functions
aCat.showMiceCaught();
aCat.catchMouse();
aCat.showMiceCaught();
aCat.changeActivity('playing');
</script>
Rather than just being pedantic, closures do have at least one very useful function. Keeping the global namespace lean.
You create lots of functions, and global variables when you create your library, but each time you declare a function or variable, you increase the risk that you’ll have a collision with another library your using. A closure on the other hand gives you encapsulation. Creating all your global elements your library needs in a closure will help isolate your library from others.
As a simple example, lets say you want to show a message after the page loads. Normally you might do this:
<script type="text/javascript">
var msg = "Welcome!";
window.onload = function(){
alert( msg );
};
</script>
What happens though if msg is already in use by another library? You’ve just clobbered it. So how do you get around this, and still use window.onload? Use a closure like this:
<script type="text/javascript">
(function(){
var msg = "Welcome!";
window.onload = function(){
alert( msg );
};
})();
</script>
Now javascript will execute the anonymous function immediately and tie the variable msg to the inner function. Later, after the page is loaded (including all the additional javascript code that might be there), the onload event is triggered, and your alert(msg) is run, BUT msg is kept out of the global scope. It’s truly private.
As a more sophisticated example, take a look at the source code for jQuery. Here the author has taken pains to not pollute the global namespace by using closures.