by William Countiss

Closing the Book on Closures

JavaScript closures are an important, but notoriously confusing concept. There’s no escaping it — if you want to grow as a developer, you need to understand what closures are and how to use them.

Don’t let the fancy name scare you — once you play around with closures a bit you’ll realize that there really isn’t much to them.

Let’s start with something simple:

  1 function sayGreeting(greeting) {  2  3     return function(name) {  4  5         console.log(greeting + " " + name);  6     }  7  8 }

You’ll notice right away that our function, sayGreeting, returns another function. I can do this in JavaScript because functions are considered first-class, which means that they can be passed around just like other data types such as a number, string, or boolean. This can make for some interesting syntax:

  1 function sayGreeting (greeting) {  2  3     return function (name) {  4  5         console.log (greeting + " " + name);  6     }  7  8 }  9 sayGreeting("Hello")("William");

So what would you expect to see in the console when we run this code? Think about it for a moment and then take a look at the image below.

If you guessed “Hello William”, you’re right. Go ahead and give yourself a pat on the back. Now, let’s take a closer look into why.

sayGreeting("Hello")("William");

Remember that sayGreeting returns a function. As we mentioned earlier, functions in JavaScript are first-class, and may be passed around like any other data structure. So when sayGreeting(“Hello”) is invoked for the first time it executes and returns an anonymous function. A returned function may also be invoked, and that is why you are seeing the second set of parentheses: sayGreeting(“Hello”)(“William”)

To make this a bit easier to follow let’s change the code a little by setting the first invocation to a variable:

  1 function sayGreeting (greeting) {  2  3     return function(name) {  4  5         console.log(greeting + " " + name);  6     }  7  8 }  9 10 var sayHello = sayGreeting("Hello"); 11 sayHello("William");

If you run this in your console you’ll get the same result as before. But how does sayHello(“William”) know about the value of the parameter greeting from the sayGreeting function? To understand this, we’ll need to go a little deeper.

Whenever a function is invoked, memory is set aside for that function and its contents, which stick around even after the function has finished executing. We can visualize this by wrapping the sayHello variable with a console.dir()

  1 function sayGreeting(greeting) {  2  3     return function(name) {  4  5         console.log(greeting + " " + name);  6     }  7  8 }  9 10 var sayHello = sayGreeting("Hello"); 11 12 console.dir(sayHello); 13 sayHello("William");

You’ll see in the console that the variable sayHello is an anonymous function, and within its scope there is a Closure with a name:value pair,

greeting: “Hello”

This should look familiar since “greeting” is the name of the parameter of the sayGreeting(greeting) { … } function on line 1, and “Hello” was the string that we passed into it when we first invoked the function on line 10. Memory was then set aside for these values and is available as an outer reference when we invoke the function on line 13.

To help visualize this let’s write out the body of the sayHello function as it is executed on line 13.

  1 function (name) {  2  3     console.log (greeting + " " + name);  4 }

The string “William” is passed in for the name parameter, then on line 3 console.log(greeting + “ “+ name) is executed.

It then looks for the values of greeting and name.

Our function finds a value for name: “William”. But it doesn’t have a value for greeting. So now it’s time to go fishing, and it looks to its outer reference (where it sits in terms of lexical scope) in an attempt to find a value for greeting.

In other words, it remembers where it was explicitly written in the code, which is inside of the sayGreeting function.

  1 function sayGreeting(greeting) {  2  3     return function(name) {  4  5         console.log(greeting + ' ' + name);  6     }  7  8 }

When it finds the value of greeting in its outer reference, we refer to this as closing in on an outer variable, and when this happens you have closure.

That wasn’t so bad, was it?

This is a very basic example, but even in complex applications, the rules remain the same. Whenever a function can’t find the value of something within itself, it will follow the scope chain all the way down (or up depending upon how you envision it) and search for that value to create the closure.