understanding javascript closures

Closures, Lambda, Anonymous functions or whatever else you may call them have become commonplace in modern Javascript, thanks in large part to libraries like Prototype and jQuery. There are many ways to write closures and some common design patterns that utilize closures, I will do my best to explain the what and why.

Required Knowledge

Understanding variable scope in JavaScript, especially the global namespace isĀ imperativeĀ to employing closures. Let’s walk through a quick example:

<code><script type="&quot;text/javascript">
var first = 1, second = 2, third = 3; 

// notice the ways I'm referencing the variables in the console log
console.log("first: %i | second: %i | third: %i", first, window['second'], window.third); 

// variables within a function
var somefunction = function(){   
    fourth = 4; // no 'var' keyword and this enters the global scope   
    inner_function = function(){ console.log('o nose! I am global'); }; 
}(); // auto-invoke just for brevity's sake 

// output: Fourth is 4 
console.log("Fourth is %i", window.fourth);  

// output: 'o nose! I am global'
inner_function();
</script></code>

The point is that it’s important to understand what scope your variables exist in and the importance of using ‘var’ to control scope; this is probably one of the more difficult aspects of the language. Variables enter the global scope easily and the lookup chain for variables always works its way up to ‘window’ or the global scope (psuedocode example:
while( variable not defined in current scope && scope != window ) { check_parent_scope( variable ) )

Syntax

There are a few ways to write closures:

<code><script type="&quot;text/javascript">
// self-invoking closures are useful when you don't want the user (person including the script) to have access to any of your data/methods
(function(msg){ console.log(msg) })("Hello World"); 
// similar to self-invoking closure, but more favorable because it fails more gracefully
!function(msg){ console.log(msg) }("Hello World"); 
// callbacks passed to functions 
each(array, function(item){ ... });
</script></code>

Usage

I’m not going to get into every use case for closures but will cover my most common use.

Library Development

I end up developing a lot of libraries for Answers.com, side projects and experimentation and find the closure pattern to make for good APIs to that library. I have a lot of internal logic that I don’t want to expose and would prefer to have private scoping. Here is a quick example that provides an abstraction layer for HTML5 storage (sessionStorage and persistent localStorage) that automatically serializes/deserializes objects and arrays.

<code>
// use of local/sessionStorage without library
localStorage.setItem('myKey', 12345);
sessionStorage.setItem('myKey', 12345);
localStorage.getItem('myKey');
</code>
<code>
var storage = (function(){
        // this scope will not be accessible once the containing function returns, i.e. private scope
        var _store = function(engine, key, value){
                if(value){
                    value = JSON.stringify( value );
                    return window[engine].setItem(key, value)
                }
                return JSON.parse(window[engine].getItem(key)) || null;
        }

        // the scope of the object returned retains access to the function scope
        return {
                session: function(key, value){
                        return _store('sessionStorage', key, value);
                },
                persistent: function(key, value){
                        return _store('localStorage', key, value);
                }
        };
})();

var user = {
        first_name: 'Rob',
        city: 'St. Louis'
}

storage.session('user', user);
storage.persistent('user', user);

console.log("First name is: %s", storage.session('user').first_name);
</code>

Anonymous functions as arguments

Allowing callback arguments in your functions promotes reuse, let’s users hook into the private data and control the side-effects of your library, let’s extend the previous storage example with an each method that accepts a callback:
View Gist

<code>
var storage = (function(){
	// this scope will not be accessible once the containing function returns, i.e. private scope
	var _store = function(engine, key, value){
		if(typeof value !== undefined && value){
			if(typeof value !== 'string'){
				value = JSON.stringify( value );
			}
			return window[engine].setItem(key, value)
		}
		return JSON.parse(window[engine].getItem(key)) || null;
	}

	// the scope of the object returned retains access to the function scope
	return {
		session: function(key, value){
			return _store('sessionStorage', key, value);
		},
		persistent: function(key, value){
			return _store('localStorage', key, value);
		},
		each: function(key, callback, engine) {
			// default value for storage engine
			engine = engine || 'localStorage'; 
			// ensure a valid value was passed, return if not
			if ( ['localStorage', 'sessionStorage'].indexOf( engine ) === -1 ) return false;
			
			var data = _store(engine, key, undefined);
			
			// no need to iterate over the object if the callback passed is not a function
			if(typeof callback === 'function' && data) {
				// data is an array
				if('splice' in data){
				    // loop over array and apply callback to each item
					for( var i = 0; i < data.length; i++ ){
						var current_record = data[i];
						// though we could use the syntax: callback(data), I prefer to additionally provide access to the data in `this`
						// check the usage examples for how this looks when calling the each method
						callback.apply(current_record, [ current_record, i ]);
					}
				} else {
				    // loop over object and apply callback to each item
					for( var attribute in data ){
						// now I am passing the value @index as well as the name of the attribute to the callback
						callback.apply(data[attribute], [ data[attribute], attribute ])
					}
				}
			}
			return this;
		}
	};
})();

var user = {
		first_name: 'Rob',
		city: 'St. Louis'
	},
	user2 = {
		first_name: 'Jeb',
		city: 'Nashville'
	}

storage.persistent('user', user);
storage.persistent('users', [ user, user2 ]);

storage.each('user', function(item, index){
	console.log("%s => %s", index, item);
});

storage.each('users', function(item, index){
	console.log("%s => { %s | %s }", index, this.first_name, item.city);
});
</code>

Leave a Reply