JavaScript Closures

What is a Closure?

A closure is a function that generates a function. Each closure has its own scope and does not in and of itself create a function. Rather, it creates a template for a function that can be reused to create boilerplate JavaScript code for many similar situations.

Consider a simple case where we want to say hi to a user based on his/her name. This is the “traditional” way without a closure:

function sayHi(user) {
	document.getElementById("greeting").innerHTML = "Hi, " + user;
}

The same use case with a closure will return a function that is executed as needed:

function makeSayHi(user) {
	return(function() {
		document.getElementById("greeting").innerHTML = "Hi, " + user;
	});
}
var sayHiToColby = makeSayHi("Colby");
sayHiToColby();

Purpose of Closures

So what's the use of a closure? For a single element, closures are more work than the “traditional” approach. Now imagine you have a large array of users, each of which who speak a different language. In the traditional approach one would need to write tons of functions calls such as:

function sayHiMultilingual(prefix, user, suffix) {
	documentgetElementById("greeting").innerHTML = prefix + user + suffix;
}
sayHiMultilingual("Hi ", user, ""); // English
sayHiMultilingual("今日は", user, "さん!"); // Japanese
sayHiMultilingual("नमस्ते ", user, ""); // Hindi

Using closures, one can boilerplate all these different languages into one function generator that boilerplates each language:

function sayHiMultilingualClosure(prefix, suffix) {
	return (function(user) {
		document.getElementById("greeting").innerHTML = prefix + user + suffix;
	});
}
var sayHiEnglish = sayHiMultilingualClosure("Hi, ", "");
var sayHiJapanese = sayHiMultilingualClosure("今日は", "さん!");
var sayHiHindi = sayHiMultilingualClosure("नमस्ते ", "");

The great advantage of closures is now three functions now exist to greet the user: English, Japanese, and Hindi. The closure has centralized the “boilerplate” of the individual languages and isolated the only varying the parameter: the user's name. Now, all one has to execute are calls such as:

  • sayHiEnglish("Colby");
  • sayHiJapanese("上田智子");
  • sayHiHindi("विमल कुमार");

Now, no matter what language the user speaks, the user will be greeted in their native language.

Case Study: Create Bootstrap Grids

Change Number of Columns: Traditional Way


Column 1
Column 2
Column 3

Change Number of Columns: Closures


Column 1
Column 2
Column 3

Change Number of Columns: Source Code

/**
 * "traditional" - non-closure way of taking input modifying HTML
 *
 * @param numCols number of Bootstrap columns to generate
 **/
function setNumberOfColumns(numCols) {
	var suffix = Math.floor(12 / numCols);
	var html = "";
	for(var i = 1; i <= numCols; i++) {
		html = html + "<div class=\"col-sm-" + suffix + "\">Column " +  i + "</div>";
	}
	document.getElementById("traditionalRow").innerHTML = html;
}

/**
 * closure that generates a function that will generate a dynamic number of columns
 *
 * @param numCols number of Bootstrap columns to generate
 * @return function closure that will generate the number of columns specified by numCols
 **/
function makeColumns(numCols) {
	return(function() {
		var suffix = Math.floor(12 / numCols);
		var html = "";
		for(var i = 1; i <= numCols; i++) {
			html = html + "<div class=\"col-sm-" + suffix + "\">Column " +  i + "</div>";
		}
		document.getElementById("closureRow").innerHTML = html;
	});
}


/**
 * function to boilerplate the selection of number of columns generated from the closure; this function is loaded with the page
 **/
function loadClosure() {
	// boilerplate the HTML links
	var linkHTML = "<p>Number of columns:</p><ul class=\"list-inline\">";
	for(var i = 1; i <= 12; i++) {
		linkHTML = linkHTML + "<li><a href=\"#closureRow\" id=\"closure-" + i + "\">" + i + " Columns</a></li>";
	}
	linkHTML = linkHTML + "</ul>";
	document.getElementById("closureOptions").innerHTML = linkHTML;

	// use a for loop to add the onClick event to each link in the previous for loop
	for(i = 1; i <= 12; i++) {
		document.getElementById("closure-" + i).onclick = makeColumns(i);
	}
}

/**
 * jQuery command to load the loadClosure() function when the page is ready
 **/
$(document).ready(function() {
	loadClosure();
});