Felix Kling

JavaScript: About loops, functions and closures

On Stack Overflow, nearly every day (at least it feels like so) I find questions about why some JavaScript code gives unexpected results - and the reason is almost always the same.

Let me take you on a short journey about loops, functions and closures.

Loops #

A for loop in JavaScript seems to be the same as a for loop in C or Java, but it is not. It is actually more like in PHP.

The most important thing to know about loops in JavaScript is that they do not create a scope. JavaScript does not have block scope, only function scope. What does this mean?

Consider the following snippet:

function foo() {
  var bar = 1;
  for (var i = 0; i < 42; i++) {
    var baz = i;
  }
  /* more code */
}

It is clear that bar will be available in the whole function. But baz (and i) will be too! There is only one difference: Until the first iteration of the loop, baz will have the value undefined. After the loop, it will have the value 41 (and i will be 42).

So any variable you declare anywhere in a function will be available everywhere in the function, but it will only have a value after one was assigned to it. The above is equivalent to:

function foo() {
  var bar = 1,
      baz, i;
  for (i = 0; i < 42; i++) {
    baz = i;
  }
  /* more code */
}

Lets continue.

Functions #

Functions are powerful in JavaScript. Because they are first-class citizens, you can pass them around like any other value. Therefore you can also create functions inside functions and return them, e.g.:

function foo() {
  return function(x) {
    return 42 * x;
  }
}

Which brings us to the next point.

Closures #

Closures are nothing else then functions defined in other functions that are passed to some other context. They are called closures because they close over the local variables of the function they are defined in (i.e. they have access to the other functions scope).

Again an example:

function foo(x) {
  return function() {
      return 42 * x;
  }
}

This time, x defined as parameter of foo, and var bar = foo(2)(); would return 84. The inner function, the function that is returned by foo has access to x.

Functions inside loops #

Why is all this important? Because it gives those who want to create functions that depend on loop variables a hard time. Consider this snippet which assigns an click handler to various elements:

// elements is an array of 3 DOM elements
var values = ['foo', 'bar', 'baz'];

for (var i = 0, l = elements.length; i < l; i++) {
  var data = values[i];
  elements[i].onclick = function() {
    alert(data);
  };
}

What is the value they elements will alert when they are clicked? It will be the same for all, namely baz. Here is the reason again: By the time, the event handler is called, the for has already finished. JavaScript has no block scope, i.e. all the handlers share a reference to the same data variable. After the loop, this value will be values[2] which is baz. You can also think this way: Every variable declaration in a function creates one place in the memory to store the data. In the for this data is just changed over and over again, the position in the memory stays the same. Every event handler access the same position in the memory.

How to fix this? #

The only solution is to introduce another scope that “captures” the current value of data. JavaScript has only function scope. So we have to introduce another function. We could do something like:

function createEventHandler(x) {
  return function() {
    alert(x);
  }
}

for (var i = 0, l = elements.length; i < l; i++) {
  var data = values[i];
  elements[i].onclick = createEventHandler(data);
}

This works, because the value of data will be stored in the local scope of createEventHandler and this function is executed newly in every iteration.

This can be written shorter using immediate executing functions:

for (var i = 0, l = elements.length; i < l; i++) {
  var data = values[i];
  elements[i].onclick = (function(x) {
    return function() {
      alert(x);
    };
  }(data));
}

This might look special but it really is not. It is just substituting the createEventHandler by the function definition.


Further information #