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.

Leave a Reply

Your email address will not be published. Required fields are marked *