I Finally Understand Closures!

Since I started using Node I've fallen in love with JavaScript as a language. Before Node I liked JavaScript well enough, but without my trusty JQuery library it never impressed me much at all. Node has forced me to actually build a proper understanding of the JavaScript language itself. I've poured over several books, asked many Stack Overflow questions, started quite a few practice projects, and now I think I can safely say I have a pretty solid grasp of JavaScript as a language.

One of the hardest concepts for me to wrap my brain around while learning JavaScript was the concept of closures. Actually, the concept of a closure isn't really that difficult but sometimes I can make something more complicated in my head than it really is. The first thing to know about closures is this:

function getFunc() {  
    var message = "What would happen if closures didn't exist?";
    return function () {
        console.log(message);
    }
}
var showMessage = getFunc();  
showMessage();  

Without knowing what closures really are yet, pretend for a minute that they don't exist. What do you think would happen if we ran the above code? You would see undefined printed to the console window. getFunc() returns a function which we assigned to the variable showMessage. On the next line we attempt to invoke showMessage() which then tries to write the value of a variable called message to the console. The problem here is that message only existed inside of getFunc(), but showMessage() exists outside of our getFunc() function. Without closures the message variable would have no value when we try to invoke showMessage().

Lucky for us closures do exist. Technically every function is a closure; each function creates a variable scope. In our example var message was defined within the scope of getFunc(); You might think that when getFunc() returns our scope would be lost, along with our message variable. However, in our example we define a nested function and return it. Our nested function contains a reference to message which lives in the same scope as the nested function. When our nested function is defined, the current scope is bound to the nested function. This means that the message variable still exists as long as our nested function still exists, which it does because we return it and assign it to a variable in the outer scope. When our example runs you would see the string printed to the console rather than undefined.

It's important to note that you could continue nesting functions as far as you wanted. The parent scopes of each function are all accessible within each nested function.

function func1() {  
    var myVar1 = true;
    function func2() {
        var myVar2 = 123;
        // has access to myVar1 and myVar2.
        function func3() {
            var myVar3 = 'hello world.';
            // has access to myVar1, myVar2, and myVar3;
        }
    }
    // has access to myVar1
}

In the above example func3 has access to all three scopes: the current scope, the parent (func2) scope, and the parent's parent scope (func1). This is known as the scope chain. The way a scope chain becomes bound to a function and goes with it wherever it goes is known as a closure. Worded another way, the function closes over all the variables in the scope chain that it has access to, thus we get a closure.

Okay great, you know what a closure is but what is it useful for? Closures are such a big deal in JavaScript because they provide access to private variables. Private variables are just what we call variables defined within a scope that is inaccessible to us. Let's look at an example function that creates an object representing a Car.

function Car() {  
    var honkCount = 0;
    this.honk = function () {
        console.log("Honk honk!");
        honkCount++;
    };
    this.getHonkCount = function () {
        return honkCount;
    };
}

var newCar = new Car();  
console.log(newCar.getHonkCount()); // Displays: 0  
newCar.honk(); // Displays: "Honk honk!"  
console.log(newCar.getHonkCount()); // Displays: 1  

In the above example honkCount is not accessible outside of the Car() constructor. For all intents and purposes honkCount is a private variable. However, notice that we expose two methods on the object. These two methods close over the scope within the constructor, preserving access to all variables within that scope. When we call newCar.honk() or newCar.getHonkCount() both of those methods have access to honkCount because they are each closures around the scope in which they were defined. This means that from outside the constructor function we can use the public methods on our new object and those methods have access to the private variable even though we cannot access it directly ourselves.

I probably read a dozen explanations of closures not too dissimilar from this post and it still took me a while to get it. So don't be too discouraged if you still aren't fully wrapping your brain around it. Just keep reading and practicing and you'll have that light bulb moment before you know it.

Chev

Read more posts by this author.

comments powered by Disqus