2

My code:

for (var i = 0; i < mapInfos.length; i++) {

            var x = function () { doStuff(i); };
            google.maps.event.addListenerOnce(mapInfos[i].map, 'tilesloaded', x);
}

The doStuff method simply alerts the value of i. mapInfos has two entries, so you'd expect it to alert 0 and 1, but instead it alerts 2 and 2. I can appreciate vaguely why it is doing this (although var i should keep it local to the scope of the loop?) but how can I make it work as intended?

2
  • 1
    Doh - I can't even guess how often this question was asked before. Commented Jul 21, 2011 at 14:44
  • Your original jsfiddle was a good example. Commented Jul 21, 2011 at 14:47

5 Answers 5

8

edit — note that when first posted, the original question included a link to a jsfiddle that seemed to be a relevant example of what the current question is trying to achieve, only it appears to work ...


The code in the jsfiddle works because there's only one "i" in that code. The "i" used in the second loop (where the functions are actually called) is the same "i" as used in the first loop. Thus, you get the right answer because that second loop is running "i" through all the values from zero through four again. If you added:

i = 100;
functions[0]();

you'd get 100 printed out.

The only way to introduce a new scope in JavaScript is a function. One approach is to write a separate "function maker" function:

function makeCallback(param) {
  return function() {
    doStuff(param);
  };
}

Then in your loop:

for (var i = 0; i < mapInfos.length; i++) {
  var x = makeCallback(i);
  google.maps.event.addListenerOnce(mapInfos[i].map, 'titlesloaded', x);
}

That'll work because the call to the "makeCallback" function isolates a copy of the value of "i" into a new, unique instance of "param" in the closure returned.

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

4 Comments

What jsfiddle? What second loop?
@Quentin there was a jsfiddle link when the question was first posted. It's linked in a comment I added to the question. It's an interesting piece of code :-)
Sorry, I quickly noticed the same thing as the person who answered put, and removed the JSFiddle because it didn't corroborate the rest of my question:)
The function factory worked a treat - the initial solution I found on google was to use new function() { } but this does -not- work properly and the function factory is much better!
1

Create a new scope for it.

Functions create scope.

function doStuffFactory(i) {
    return function () { doStuff(i); };
}

for (var i = 0; i < mapInfos.length; i++) {
    var x = doStuffFactory(i);
    google.maps.event.addListenerOnce(mapInfos[i].map, 'tilesloaded', x);
}

6 Comments

eehhr...google.maps.event.addListenerOnce(mapInfos[i].map, 'tilesloaded', doStuff(i)); ?
@jAndy: You're now passing the result of calling the function, not a reference to it.
@Tomalak: fair enough, note myself.
@jAndy — No. The original code has the 3rd argument as a function. I'm assuming that doStuff doesn't return one. You could put the call to doStuffFactory there directly instead of going via x if you like though.
@HoLyVieR — your answer also also creates a function each time it loops.
|
1

Change it to

var x = function (param) { doStuff(param); };

Obviously what is going on is that you are alerting a variable that is changing. With the above change it copies it so even if i changes it will still alert the right value.

Comments

0

Javascript doesn't have block scope, so you don't get an x that's local to the loop. Yea!

It has function scope, though.

Comments

0

Yep, weird isn't it!Pointy has an explanation

I have no idea why your first example worked (I wasn't expecting it to) Pointy has an explanation of why your first example worked - The reason why your second one doesn't is because i is scoped to the function containing the for loop, not to the scope defined by the for loop. In fact the only things that have scope in JavaScript are functions. This means that by the time your function gets executed i is 2.

What you need to do is create a scope, for example:

for (var i = 0; i < mapInfos.length; i++) {

    var x = (function() {
        return function () { doStuff(i); };
    })(i);
    google.maps.event.addListenerOnce(mapInfos[i].map, 'tilesloaded', x);
}

See JavaScript closures in for-loops for more.

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.