Categories
closures javascript loops

JavaScript closure inside loops – simple practical example

3129

var funcs = [];
// let's create 3 functions
for (var i = 0; i < 3; i++) {
  // and store them in funcs
  funcs[i] = function() {
    // each should log its value.
    console.log("My value: " + i);
  };
}
for (var j = 0; j < 3; j++) {
  // and now let's run each one to see
  funcs[j]();
}

It outputs this:

My value: 3
My value: 3
My value: 3

Whereas I’d like it to output:

My value: 0
My value: 1
My value: 2


The same problem occurs when the delay in running the function is caused by using event listeners:

var buttons = document.getElementsByTagName("button");
// let's create 3 functions
for (var i = 0; i < buttons.length; i++) {
  // as event listeners
  buttons[i].addEventListener("click", function() {
    // each should log its value.
    console.log("My value: " + i);
  });
}
<button>0</button>
<br />
<button>1</button>
<br />
<button>2</button>

… or asynchronous code, e.g. using Promises:

// Some async wait function
const wait = (ms) => new Promise((resolve, reject) => setTimeout(resolve, ms));

for (var i = 0; i < 3; i++) {
  // Log `i` as soon as each promise resolves.
  wait(i * 100).then(() => console.log(i));
}

It is also apparent in for in and for of loops:

const arr = [1,2,3];
const fns = [];

for(var i in arr){
  fns.push(() => console.log(`index: ${i}`));
}

for(var v of arr){
  fns.push(() => console.log(`value: ${v}`));
}

for(var f of fns){
  f();
}

What’s the solution to this basic problem?

13

2343

Well, the problem is that the variable i, within each of your anonymous functions, is bound to the same variable outside of the function.

ES6 solution: let

ECMAScript 6 (ES6) introduces new let and const keywords that are scoped differently than var-based variables. For example, in a loop with a let-based index, each iteration through the loop will have a new variable i with loop scope, so your code would work as you expect. There are many resources, but I’d recommend 2ality’s block-scoping post as a great source of information.

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

Beware, though, that IE9-IE11 and Edge prior to Edge 14 support let but get the above wrong (they don’t create a new i each time, so all the functions above would log 3 like they would if we used var). Edge 14 finally gets it right.


ES5.1 solution: 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. That is, assuming you’ve got some sort of array containing values (DOM references, objects, whatever), and the problem arises of setting up callbacks specific to each element, you can do this:

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

The idea is that each invocation of the callback function used with the .forEach loop will be its own closure. The parameter passed in to that handler is the array element specific to that particular step of the iteration. If it’s used in an asynchronous callback, it won’t collide with any of the other callbacks established at other steps of the iteration.

If you happen to be working in jQuery, the $.each() function gives you a similar capability.


Classic solution: Closures

What you want to do is bind the variable within each function to a separate, unchanging value outside of 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]();
}

Since there is no block scope in JavaScript – only function scope – by wrapping the function creation in a new function, you ensure that the value of “i” remains as you intended.

7

  • 11

    isn’t function createfunc(i) { return function() { console.log("My value: " + i); }; } still closure because it uses the variable i?

    – Incerteza

    Mar 28, 2014 at 3:45

  • 62

    Unfortunately, this answer is outdated and nobody will see the correct answer at the bottom – using Function.bind() is definitely preferable by now, see stackoverflow.com/a/19323214/785541.

    Jun 20, 2014 at 12:21

  • 96

    @Wladimir: Your suggestion that .bind() is “the correct answer” isn’t right. They each have their own place. With .bind() you can’t bind arguments without binding the this value. Also you get a copy of the i argument without the ability to mutate it between calls, which sometimes is needed. So they’re quite different constructs, not to mention that .bind() implementations have been historically slow. Sure in the simple example either would work, but closures are an important concept to understand, and that’s what the question was about.

    Jul 12, 2014 at 2:35

  • 11

    Please stop using these for-return function hacks, use [].forEach or [].map instead because they avoid reusing the same scope variables.

    Feb 7, 2015 at 10:23

  • 46

    @ChristianLandgren: That’s only useful if you’re iterating an Array. These techniques aren’t “hacks”. They’re essential knowledge.

    – user1106925

    Jun 29, 2015 at 16:31

409

Try:

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):

Personally I think @Aust’s more recent answer about using .bind is the best way to do this kind of thing now. There’s also lo-dash/underscore’s _.partial when you don’t need or want to mess with bind‘s thisArg.

4

  • 5

    any explanation about the }(i)); ?

    – aswzen

    Apr 6, 2018 at 1:32

  • 3

    @aswzen I think it passes i as the argument index to the function.

    – Jet Blue

    Jul 26, 2018 at 22:01

  • it is actually creating local variable index.

    Mar 15, 2019 at 15:17

  • 2

    Immediately Invoke Function Expression, aka IIFE. (i) is the argument to the anonymous function expression that is invoked immediately and index becomes set from i.

    – Eggs

    Apr 14, 2020 at 5:51

380

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

As pointed out by @squint and @mekdev, you get better performance by creating the function outside the loop first and then binding the results within 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]();
}

6

  • This is what I do these days too, I also like lo-dash/underscore’s _.partial

    – Bjorn

    Dec 8, 2014 at 5:18

  • 20

    .bind() will be largely obsolete with ECMAScript 6 features. Besides, this actually creates two functions per iteration. First the anonymous, then the one generated by .bind(). Better use would be to create it outside the loop, then .bind() it inside.

    – user1106925

    Jun 28, 2015 at 3:29

  • 5

    @squint @mekdev – You both are correct. My initial example was written quickly to demonstrate how bind is used. I’ve added another example per your suggestions.

    – Aust

    Jun 29, 2015 at 16:23

  • 5

    I think instead of wasting computation over two O(n) loops, just do for (var i = 0; i < 3; i++) { log.call(this, i); }

    Sep 11, 2015 at 12:14


  • 1

    .bind() does what the accepted answer suggests PLUS fiddles with this.

    – niry

    Jan 8, 2017 at 5:55