-1

Possible Duplicate:
javascript for loop unexpected behaviour

I'am programming in node.js and i run into a small problem what is caused by some strange pass by reference problem.

i use the following code:

for(j in groups){
  console.log(j, somefunction(groups[j]))
}

inside of the function some funky things are done with the data but afterwards i get the following as result:

3 data
3 data
3 data
3 data
3 data
3 data

instead of

1 data
2 data
3 data
4 data
5 data
6 data

but it keeps the correct amount of result..

Community
  • 1
  • 1
Spidfire
  • 5,433
  • 6
  • 28
  • 36
  • Two things: 1. Your quoted code does not do that (you lost something in the simplification for the question -- easy to do!), and 2. JavaScript does not have any form of pass by reference. All arguments are passed by *value*. Objects (including functions) are referred to by reference, which has nothing to do with "pass by reference". – T.J. Crowder Jul 24 '12 at 15:40

3 Answers3

1

Your quoted code doesn't do what you've listed, you probably lost something simplifying it for the question.

This code would have the effect you're describing, though:

for(j in groups){
  doSomething(function() {
    // Callback for `doSomething`
    console.log(j, somefunction(groups[j]));
  });
}

Note that now I'm creating a function (a callback for whatever doSomething does), not just calling one.

The reason that happens is that the function being created has an enduring reference to the j variable (and everything else in the execution context), not a copy of it as of when the function was created. It's called a closure over that execution context. So your loop runs, a series of calls to doSomething start a series of things, and then later when the callbacks are called, j is 3 and so it's the same for all of them.

The usual solution is to create functions that close over something that won't change before they get called, like this:

for(j in groups){
  doSomething(makeCallback(j));
}

function makeCallback(jarg) {
    return function() {
        console.log(jarg, somefunction(groups[jarg]));
    };
}

Now, we're using jarg in our callback, rather than j. Because jarg is part of the context of the call to makeCallback, it doesn't change between when we create the function and when it's called later.

Another way you can bind data to functions is to use Function#bind (which effectively creates a closure in the background), which is an ES5 feature available in NodeJS because the V8 engine has it. Here's how that would look:

for(j in groups){
  doSomething(function(jarg) {
    // Callback for `doSomething`
    console.log(jarg, somefunction(groups[jarg]));
  }.bind(this, j)); // <== Note the bind
}

or less confusingly (and quite possibly more efficiently):

for(j in groups){
  doSomething(handler.bind(this, j));
}

function handler(jarg) {
  // Callback for `doSomething`
  console.log(jarg, somefunction(groups[jarg]));
}

More about closures:

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
0

ive found if i use var j = 0

for(i in groups){
j = i

the system does work how but it's not the best awnser

Spidfire
  • 5,433
  • 6
  • 28
  • 36
0

Do it this way :-


    for(var j in groups)
    {
        //Your code
    }

j is not returning the correct value because you have assigned j at a global scope. Hope this help!

spaceman12
  • 1,039
  • 11
  • 18
  • We don't know that there isn't a `var j;` above the code he just hasn't quoted, nor that (if there isn't) j is ever used for anything else (e.g., it could be an implicit global without actually being the problem). It would take a very specific bit of unseen code to set `j` to exactly what's quoted. Occams's razor says...not so much. – T.J. Crowder Jul 24 '12 at 22:00