JavaScript closure inside loops – simple practical example

Solution:

The problem is that the variable in each anonymous function, i, is bound to the same variable as the function.

Let’s talk about ES6.

ECMAScript 6 introduces let and const keywords. These keywords are different from var-based variables. In a loop that uses a  let-based index, for example, every iteration of the loop will be accompanied by a new variable called i, This allows your code to work as expected. There are many resources available, but 2ality’s block scoping post is a great resource.

for (let i = 0; i < 3; i++) {
  funcs[i] = function() {
    console.log("My value: " + i);
  };
}

Attention, Edge 9-IE11, Edge before Edge 14, and Edge before Edge 14 support let  it but get the above wrong. They don’t create a brand new i every time so all the functions listed above would log three, just like if we used  var). Edge 14 finally does it right.

Solution to ES5.1: forEach

With the relatively widespread availability of the  Array.prototype.forEach  function (in 2015), it’s worth noting that in those situations involving iteration primarily over an array of values, .forEach()  provides a clean, natural way to get a distinct closure for every iteration. This means that if you have an array of values (DOM references or objects), you don’t need to set up callbacks for each element.

var someArray = [ /* whatever */ ];
// ...
someArray.forEach(function(arrayElement) {
  // ... code code code for this one element
  someAsynchronousFunction(arrayElement, function() {
    arrayElement.doSomething();
  });
});

Each invocation of the callback function that is used with the loop .forEach  will have its own closure. The array element for that step of the iteration is the parameter that is passed to that handler. It won’t interfere with any other calls made at the same step of the iteration if it’s used in an Asynchronous Callback.

You can use the same  $.each()  function if you are working in jQuery.

Closures are the solution

You want to bind each variable in a function to a distinct, unchanging value outside the function.

var funcs = [];

function createfunc(i) {
  return function() {
    console.log("My value: " + i);
  };
}

for (var i = 0; i < 3; i++) {
  funcs[i] = createfunc(i);
}

for (var j = 0; j < 3; j++) {
  // and now let's run each one to see
  funcs[j]();
}

JavaScript does not have a block scope, but only function scope. By wrapping the function in a new function you can ensure that “i” is the same value as it was intended.

Try this:

var funcs = [];
    
for (var i = 0; i < 3; i++) {
    funcs[i] = (function(index) {
        return function() {
            console.log("My value: " + index);
        };
    }(i));
}

for (var j = 0; j < 3; j++) {
    funcs[j]();
}

Edit(2014)

@Aust’s recent answer regarding using .bind seems to me the best way to do it right now. You can also use  _.partial  for lo-dash/underscore when you don’t want to mess around with  bind‘s thisArg.

Another way that hasn’t been mentioned yet is the use of  Function.prototype.bind

var funcs = {};
for (var i = 0; i < 3; i++) {
  funcs[i] = function(x) {
    console.log('My value: ' + x);
  }.bind(this, i);
}
for (var j = 0; j < 3; j++) {
  funcs[j]();
}

UPDATE

@squint & @mekdev pointed out that you can get more performance by creating the function outside of the loop and binding the results inside the loop.

function log(x) {
  console.log('My value: ' + x);
}

var funcs = [];

for (var i = 0; i < 3; i++) {
  funcs[i] = log.bind(this, i);
}

for (var j = 0; j < 3; j++) {
  funcs[j]();
}

 

Exit mobile version