Accessing loop iteration in a sub-function?

2019-07-19 20:49发布

问题:

I'm using the Google Maps API to plot several points on a map. However, in the click event function below, i is always set to 4, i.e. its value after iterating the loop:

// note these are actual addresses in the real page
var addresses = new Array( "addr 1", "addr 2", "addr 3", "addr 4" );

for (var i = 0; i < addresses.length; i++) {
    geocoder.getLatLng(addresses[i], function(point) {
        if (point) {
            var marker = new GMarker(point);
            map.addOverlay(marker);
            map.setCenter(point, 13);

            GEvent.addListener(marker, "click", function() {
                // here, i=4
                marker.openInfoWindowHtml("Address: <b>" + addresses[i] + "</b>");
            });
        }
    });
}

So when the marker displays it's using addresses[4] which is undefined. How do I pass the correct value of i to the function?

回答1:

You need to generate an anonymous function during the current iteration, the following should fix it:

// note these are actual addresses in the real page 
var addresses = new Array( "addr 1", "addr 2", "addr 3", "addr 4" ); 

for (var i = 0; i < addresses.length; i++) { 
    geocoder.getLatLng(addresses[i], function (current) { 
        return function(point) { 
            if (point) { 
                var marker = new GMarker(point); 
                map.addOverlay(marker); 
                map.setCenter(point, 13); 

                GEvent.addListener(marker, "click", function() { 
                    // here, i=4 
                    marker.openInfoWindowHtml("Address: <b>" + addresses[current] + "</b>"); 
                }); 
            }
        } 
    }(i)); 
} 

B// note these are actual addresses in the real page var addresses = new Array( "addr 1", "addr 2", "addr 3", "addr 4" );

for (var i = 0; i < addresses.length; i++) { geocoder.getLatLng(addresses[i], function(point) { if (point) { var marker = new GMarker(point); map.addOverlay(marker); map.setCenter(point, 13);

        GEvent.addListener(marker, "click", function() { 
            // here, i=4 
            marker.openInfoWindowHtml("Address: <b>" + addresses[i] + "</b>"); 
        }); 
    } 
}); 

}

Further Clarification
The getLatLng method provided by Google uses an ajax call to get the lat and long for a specific address. Since this is an asynchronous call and is not part of the current thread, a callback function is required which is called on completion of the ajax request. This is the anonymous function you specify as the second parameter of the function.
Now, whilst the ajax requests are being made, your code continues to run, increasing the value of i each time the loop iterates over the array. By the time your first ajax call returns, the loop has already increased to the length of the addresses array (4), so when the your callback function runs you're retrieving the in-scope variable i after it has been increased by the loop.

With the fix I wrote, you're creating an anonymous function that takes a single argument - current - and returns the previous anonymous function with the i variable replaced with the current variable. This function is invoked right away, before the next iteration of the loop, with the i variable as it's first parameter. This creates a closure for which the current value of i is stored in the current variable at the time the function is called. When we refer to the current variable later, we're getting the stored value of i.

I'm not really very good at explaining these sort of things, probably because my understanding of it isn't quite as good as the js Gods. Better to read some more info on javascript closures.



回答2:

i should not reach 4 in that loop. The loop runs so long as i < addresses.length (which is 4), so the loop should run from i=0 to i=3.