5

Possible Duplicate:
Javascript infamous Loop problem?

I have the following:

function test(a,b,c){
    console.log(a+b+c);
}
for (var i=0; i < array.length; i++){
    steps.push(
        function(){ test(array[i].something, array[i].wow, i);}
    );

I want to store functions with several parameters, and bind them to buttons later when the DOM is loaded

for (var i=0; i<steps.length; i++){
    $('#mydiv li a').eq(i).click(steps[i]);
}

It's not really working since steps[i] contains do(array[i].something, array[i].wow, i); instead of do('bob', 'john', 1) for example

I have tried using the .apply javascript function but it will execute in the loop, instead of storing them

Community
  • 1
  • 1
  • 1
    note the `do` is a reserved javascript name... if you'r using this name in your code, i suggests you to change it to something else.. maybe this is the cause to your bug – udidu Feb 28 '12 at 17:25
  • ugly solution would use eval :/ – elrado Feb 28 '12 at 17:24

4 Answers4

3

Closures can help you here. Here's one solution to the first bit of code:

function do(a,b,c){
    console.log(a+b+c);
}
for (var i=0; i < array.length; i++){
    (function (i) {
        steps.push(
            function(){ do(array[i].something, array[i].wow, i);}
        );
    })(i);
}

There's no "block scope" in Javascript, just function scope. With your code, each function you pushed into the steps array had a reference to the last value of i, not the one it had when the function was pushed into steps. Invoking an immediately executing function enables the pushed function to have a value for i that doesn't change, because a closure is being created. And this closure has a value for i that is independent of the loop's value for i, which will be whatever is the last index of array.

Paul Bruno
  • 1,896
  • 1
  • 17
  • 26
  • why do you need the i parameter in each closure? –  Feb 28 '12 at 17:26
  • Because that saves a "permanent" value for `i`, that won't change, even though the loop's value for `i` will. It creates an `i` variable that is only visible and modifiable by the function you're pushing into your `steps` array. The `i` contained in the line `})(i);` is passing the loop's `i` into the function. Inside the function, the `i` referred to there is the one and only parameter of the anonymous function. Make sense? – Paul Bruno Feb 28 '12 at 17:29
  • See [this Stack Overflow question](http://stackoverflow.com/questions/111102/how-do-javascript-closures-work) for more information (on the general subject of JS closures, with references to articles and other content). – Paul Bruno Feb 28 '12 at 17:32
  • is it test.bind(null, array[i].something, array[i].wow, i); –  Feb 28 '12 at 17:35
  • You can use `bind`, but there are cross-browswer issues that you have to navigate. `Function.prototype.bind` isn't automatically defined in all browsers. Whereas, closures are an integral part of the language; the closures solution works everywhere. (Note, I believe Prototype inserts this method where it doesn't exist. jQuery's own `.bind` is for setting event handlers. (And it's now quasi-depricated.) – Paul Bruno Feb 28 '12 at 17:40
2

Use function.bind.

// Create a new function with null for the `this` object, and 1 and 2 for its arguments.
// Unlike `apply`, you shouldn't enclose the arguments in an array.
var fn = someFn.bind(null, 1, 2, 3, 4);
// This is like calling someFn.apply(null, 1, 2, 3, 4);
fn();

See the docs here. Note under the heading Compatability, it explains how to make sure this function will work on all your target browsers (even old verison of IE). You can just copy and paste the code there.

benekastah
  • 5,651
  • 1
  • 35
  • 50
  • is it test.bind(null, array[i].something, array[i].wow, i); or test.bind(null, [array[i].something, array[i].wow, i]); –  Feb 28 '12 at 17:35
  • This method wants the arguments to be listed out as normal: `test.bind(null, array[i].something, array[i].wow)`. – benekastah Feb 28 '12 at 18:38
0
//assuming it's always the same function    
for (var i=0; i < array.length; i++){
    $("#mydiv li a").eq(i).on('click',function() {
        // your function here
    });
}
Nik
  • 420
  • 1
  • 5
  • 15
  • I need to store these click functions (for reuse), either in an array, either using the onlick attribute of tags –  Feb 28 '12 at 17:49
0
function doSomething(a,b,c){
    console.log(a+b+c);
}

functionPrepare(func, args, thisarg) {
  return function() { func.apply(thisarg, args); };
)

var steps = [];
for (var i=0; i < array.length; i++) {
  steps.push( 
    functionPrepare(doSomething, [array[i].something, array[i].wow, i]) 
  );
}

// later...    
for (var i=0; i<steps.length; i++) {
  steps[i]();
}
Tomalak
  • 332,285
  • 67
  • 532
  • 628