A Deeper Look at JavaScript Closures
Don Kiely continues his discussion about the importance of JavaScript closures for developers.
August 9, 2012
In my article "JavaScript Closures," I talked about the basic concept of closures in JavaScript code. Now I'd like to invoke Mozilla's description of a closure:
A closure is a special kind of object that combines two things: a function, and the environment in which that function was created. The environment consists of any local variables that were in-scope at the time that the closure was created.
So, in the following code, newFunc is a closure that includes the displayMsg function that's defined in the createFunction function as well as the message variable and its value:
function createFunction() { var message = "JavaScript closures rock!"; function displayMsg() { console.log(message); } return displayMsg;}var newFunc = createFunction();newFunc();
You have to understand the concept of scope chains to understand closures. JavaScript is a lexically scoped language, which means that you can think of a variable's scope as a set of code statements in which a variable is defined. Global variables are scoped throughout the program code, and local variables are scoped throughout the function where they' re defined, which includes any nested function definitions.
Every JavaScript function has a scope chain that's associated with the function. The scope chain is a list of objects that defines the variables that are in scope for a chunk of code. When the code references a variable, JavaScript looks at the first object in the scope chain. If JavaScript finds the desired variable name, JavaScript then uses that variable's value. If JavaScript doesn't find the correct variable name, it continues the search with the next object in the chain. JavaScript continues this process until it finds the variable or reaches the end of the scope chain.
To make Mozilla's description more precise, all JavaScript functions are closures because they are objects that have a scope chain that's associated with them. Most of the time, functions are invoked with the same scope chain that was in effect when the code defined the function, so the same variables are in scope at function definition and invocation. However, sometimes the scope chains in effect at function definition and invocation are different. The most common situation when this occurs is when a function is nested in another function that returns the nested function, which you saw in the previous code sample.
Here's a more complex example that uses closures:
function counter() { var i = 0; return { count: function() { return i++; }, value: function() { return i; }, reset: function() { i = 0; } }}
This code creates a counter object that returns integers starting at zero and incremented by one. The counter object also has the ability to retrieve a new value while the counter increments, retrieve the current value without incrementing the counter, and reset the counter to zero. Notice that the object defines a counter variable i that the functions in the anonymous object returned by the counter function close upon.
The next example invokes the counter function twice and saves the resulting objects in the counter1 and counter2 variables, then counts up and returns the current value of each counter:
var counter1 = counter();var counter2 = counter();counter1.count();counter1.count();counter1.count();counter1.count();var c1 = counter1.value();counter2.count();counter2.count();counter2.count();counter2.reset();var c2 = counter2.value();console.log("c1 = " + c1 + ", c2 = " + c2);
The counter1 variable counts up four times, which returns a resulting value of four. The counter2 variable counts up three times, resets the counter, and returns the resulting value of zero. This shows that the counter1 and counter2 variable have their own instance of the object that's returned by the counter function with their own instances of the i counter variable.
The code is doing a couple of interesting things here:
The methods in a single instance of the object that's returned by the counter function share an instance of the i counter variable.
Each invocation of the counter function creates a new object with its own instance of the i counter variable. Operations on the counter1 and counter2 variable affect only the counter variable in the object that's associated with each variable. Another way of saying this is that each object has its own separate scope chain.
Closures are quite useful when writing JavaScript code for a web page. Closures let you write code that's more compact, readable, and maintainable, and it encourages the reuse of function code. Closures are one of the most powerful features of JavaScript, but they can also be one of the easiest features to misuse. The easiest way to abuse closures is to inadvertently create multiple closures in a loop, which lets the function share access to a variable that you didn't intend to share. Make sure you understand how to use closures properly so that you can take full advantage of them in your code. I derived this material from a JavaScript course I developed for AppDev.
About the Author
You May Also Like