5

I would like to do the something along the following:

for (var i = 0; i < 10; ++i) {
    createButton(x, y, function() { alert("button " + i + " pressed"); }
}

The problem with this is that I always get the final value of i because Javascript's closure is not by-value.
So how can I do this with javascript?

2
  • You could edit createButton, allowing it to have another argument passed, that is, i. This way you can store i in your createButton function and use it. Commented Apr 5, 2011 at 16:55
  • possible duplicate of Javascript closure inside loops - simple practical example Commented Jan 17, 2013 at 17:53

5 Answers 5

7

One solution, if you're coding for a browser that uses JavaScript 1.7 or higher, is to use the let keyword:

for(var i = 0; i < 10; ++i) {
    let index = i;
    createButton(x, y, function() { alert("button " + index + " pressed"); }
}

From the MDC Doc Center:

The let keyword causes the item variable to be created with block level scope, causing a new reference to be created for each iteration of the for loop. This means that a separate variable is captured for each closure, solving the problem caused by the shared environment.

Check out the MDC Doc Center for the traditional approach (creating another closure).

Sign up to request clarification or add additional context in comments.

Comments

6
for(var i = 0; i < 10; i++) {
    (function(i) {
        createButton(function() { alert("button " + i + " pressed"); });
    })(i);
}

Note that JSLint doesn't like this pattern. It throws "Don't make functions within a loop.".

Live demo: http://jsfiddle.net/simevidas/ZKeXX/

4 Comments

I like this answer more than Peter's due to its cleanliness. It would be nice if more browsers supported the let keyword though.
@McStretch I tried to make a let demo in jsFiddle, but I couldn't make it work. See here: jsfiddle.net/simevidas/ZKeXX/1 Firefox 4 throws an error.
@Šime - I just saw this question: stackoverflow.com/questions/2356830/… , which says you have to explicitly tell the browser (Firefox only right now) that you're using 1.7. Here's an updated fiddle: jsfiddle.net/simevidas/ZKeXX/1. Pretty lame huh? Obviously not really a good solution until everyone supports 1.7 or higher, and who know when that will happen.
@McStretch I see. The updated demo (which works in Firefox) is here: jsfiddle.net/simevidas/ZKeXX/2 It seems that we won't be able to use let for a long time. Even if IE10 implements it, we'd have to wait until IE9 exits the market (and that may not happen before 2020).
4

Create a new scope for the closure by executing another function:

for(var i = 0; i < 10; ++i) {
    createButton(x,y, function(value) { return function() { alert(...); }; }(i));
}

http://www.mennovanslooten.nl/blog/post/62

1 Comment

1. You might want to place a semi-colon at the end of the return statement to make the code a bit more readable. 2. IIFE's are usually wrapped in parens.
1

You need to put the closure into a separate function.

for(var dontUse = 0; dontUse < 10; ++dontUse) {
    (function(i) {
        createButton(x, y, function() { alert("button " + i + " pressed"); }
    })(dontUse);
}

Thise code creates an anonymous function that takes i as a parameter for each iteration of the loop.
Since this anonymous function has a separate i parameter for each iteration, it fixes the problem.

This is equivalent to

function createIndexedButton(i) {
    createButton(x, y, function() { alert("button " + i + " pressed"); }
}

for(var i = 0; i < 10; ++i) {
    createIndexedButton(i);
}

Comments

0
for(var i = 0; i < 10; ++i) {
    createButton(x, y, (function(n) {
        return function() {
            alert("button " + n + " pressed");
        }
    }(i));
}

The anonymous function on the outside is automatically invoked and creates a new closure with n in its scope, where that takes the then current value of i each time it's invoked.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.