What is Closure?
What is closure? If you've worked with languages like C and C++ before coming to JavaScript, it may be unexpected behavior. If you're learning JavaScript as your first programming language, you may not even be aware it's happening.
If we declare a global variable in JavaScript, it's accessible from within any of the functions we define and lives as long as out program is running. If we declare a variable within a function, it's only accessible from within that function and only lives until the function has finished executing.
var count = 0; // A global variable function incrementCounter() { // We can access the global "count" variable // and the local "oldValue" variable in here. var oldValue = count; count++; console.log("Count bumped from " + oldValue + " to " + count); // Once we're done in here, "oldValue" goes away. }
This behavior shouldn't be surprising, but an example of closure might be. JavaScript allows us to define functions within other functions. We can even define functions within those functions within functions and so on. These nested functions are where we can run into instances of closure.
function Counter() { this.count = 0; this.increment = function () { this.count++; } }
This is a simple constructor function for a Counter
object where the increment
function is intended to bump the value of the count
variable. But there's an alternate way to write this constructor function that may better serve its intended purpose. If someone tried to use this Counter
object as follows, they will run into a problem.
var c = new Counter(); setInterval(c.increment, 1000);
The intention is obviously to increment the counter's value every second, but can you see why this won't work? If not, you may want to review this post on using this
. Here's an alternate implementation that will work with the setInterval
example.
function Counter() { var count = 0; this.getCount = function () { return count; } this.increment = function () { count++; } }
The object's count
property has been replaced by a local variable within the constructor function and we're referencing that local variable from within the two nested functions. This is closure at work. JavaScript not only allows a function to access its own local variables, but it also allows a function to access any local variable declared within any other functions that enclose it. The getCount
and increment
functions can access their own local variables, any local variables defined in functions that contain them, and any global variables. One advantage of using closure with the count
variable is that it removes the need to use this
within the getCount
and increment
functions (so they are slightly easier to use as callback functions). Additionally, we are ensured that only functions defined within Counter
can access the count
variable. It is effectively hidden from any other functions, which means it cannot be mistakenly reset or decremented from outside Counter
. (On the down-side, we can't implement getCount
and increment
as prototype functions.)
Typically, we expect local variables to remain in scope until a function returns, then we expect them to go away. In many languages this is always the case, but it doesn't always happen with JavaScript.
function IntervalCounter(seconds) { var count = 0; var milliseconds = seconds * 1000; function getCount() { return count; } setInterval(function () { count++; }, milliseconds); }
This example defines two local variables for IntervalCounter
: count
and milliseconds
. When IntervalCounter
finishes executing, milliseconds
goes out of scope and is no longer accessible. However, closure ensures that count
will continue to be available to getCount
and the setInterval
callback function.
Closure provides an effective method of data protection in JavaScript, though it's not without its own set of risks. If you are inexperienced with JavaScript, it is possible to reference local variables that hold large objects or arrays in memory. When these variables are then referenced within enclosed functions, you may accidentally consume large blocks of memory without knowing it. As with most programming tasks, you always want to consider carefully how your program is actually working and make sure you are acting responsibly with the programming language.