jscal.es

The Closure Guide I Would've Wanted

11 months ago May 14, 2013

Closures are an important topic in JavaScript that most page-authors seem to neglect. This brief article aims to show how closures are so central to writing good JavaScript, and how you can avoid common errors when dealing with them.

I’d like to take a second to mention Secrets of the JavaScript Ninja by John Resig and Bear Bibeault. This book offers a fantastic selection of JavaScript features at an intermediate-advanced level, so definitely check it out if you’re serious about your JavaScript code.

The examples in this post are heavily inspired by those found in the book.

I am writing this article because these are the examples I would have liked to have seen when I was first searching for information on closures.

Definition


A closure is a function together with its referencing environment (the non-local variables associated with that function). Consider the following example:

function outer() {
  var someVal = 0;
  function inner() {
    return ++someVal;
  }
  return inner;
}

We see that a call to outer() will return the inner function (It’s important to note that we would not be able to call inner() on its own!) Now, if we assign the output of outer() to a variable, that variable will contain a reference to the inner function. Simple enough, so far, right?

Let’s go ahead and see what happens when we invoke the inner method.

var f = outer();
/* f is now assigned to the inner function */

for (var i = 0; i < 5; i++) {
  console.log(f());   /* invoke f and print its output */
}

Running this, we see:

1
2
3
4
5

Pretty neat, huh? Looks like we just built ourselves a counter. So why is this happening? If we take a look at our code again, we’ll see that the body of inner() contains a reference to someVal. However, someVal was defined outside of inner(), so the variable is non-local.

With regards to our definition above, someVal is contained within the closure of inner(). When inner() moves around (let’s say we passed f around from function to function), its closure goes with it. So f, no matter where it is, will be still be able to increment someVal! That’s really all there is to it.

Troubled Waters


Next, let’s take a look at what happens when a misunderstanding of closures produces strange results. Consider the following code:

for (var i = 1; i <= 5; i++) {
  setTimeout(function() {
    console.log('Iteration #' + i);
  }, 100);
}

Here we are looping through five times, creating a timeout on each turn that will display what iteration we are on. After waiting a tenth of a second, we expect to see “Iteration #1, Iteration #2, etc.” Let’s try it:

Iteration #6
Iteration #6
Iteration #6
Iteration #6
Iteration #6

Wait a second, what gives? Looking at our code (on the first loop), we see that JavaScript is creating a timeout to display “Iteration #1” in 100ms. Wrong.

Instead, we are declaring a new closure – the variable i is non-local to this inner function. When this function fires off, it won’t see the value of 1, it will see whatever value i has at that moment! By the time each of these timeouts fires off, i has the value of 6 (run through the for-loop and you’ll see why).

Our Solution


So far, we’ve diagnosed the problem and found that we are displaying “Iteration #6” each time because that’s what value i has. To fix this, we’ll need to be sure that i (or maybe some other variable) has the value we want it to have. The following code does just that, with changes highlighted in blue.

for (var i = 1; i <= 5; i++) (function(n) {
  setTimeout(function() {
    console.log('Iteration #' + n);
  }, 100);
})(i);

Let’s attempt to break this down. Instead of the traditional { ... } following the for-loop, we’ve replaced it with a self-invoking function (a function that calls itself immediately after its definition). You will see that the new body of the for-loop is a function that calls itself with the value i.

This function has its own argument n, which is entirely separated from i. We are now avoiding the problem of i changing, since we don’t use it in the function body. Instead, we send our new variable n to the timeout, and this variable doesn’t change!

The first iteration of the loop calls this function with 1, then 2, and so on and so forth. This value is then sent to the timeout, and we don’t have to worry about it changing. Instead, we are creating several independent closures. Running this, we get our expected output:

Iteration #1
Iteration #2
Iteration #3
Iteration #4
Iteration #5

Ta-da!

Considerations


Closures are useful for a variety of reasons, but the point of this post was simply to offer a quick glance into how they work and when you may run into trouble.

I hope from this point forward you are able to recognize closures in action, and are able to successfully diagnose any issues that may arise through their usage – not only in JavaScript! Visualizing scope and closures is an incredibly useful skill when running through your code, and can make the difference between good and great implementations.

I hope you enjoyed this post. Feel free to subscribe to my further musings through @jdan.