开发者

Javascript Closures and Variable Scope

I'm extending my previous question, because I still don't fully understand the concept of javascript closures. Take a quick look at the following code, which will put two markers on a map. (The code is slightly modified from my previous question).

var map = google.maps.somefunctoinstantiatemap();
var address = new Array();
address[0] = '1 Smith Street';
address[1] = '2 Smith Street';
function onpageload()
{
for(var rownum=0; rownum<address.length; rownum++)
{
    geocoder.geocode({
        'address': address[rownum]
    }, function(results, status) {
        geocodeCallBack(results,status,rownum)
    });
}
}

function geocodeCallBack(results, status, argnum)
{
    var marker = new google.maps.Marker({
        map: map,
        position: results[0].geometry.location,
        title: 'arg: '+argnum+' addr:'+results[0].formatted_address
    });

    google.maps.event.addListener(marker, 'click', function(){
        var 开发者_运维问答infowindow = new google.maps.InfoWindow({
            content: marker.title
        });
        infowindow.open(map, marker);
    });
}

Ok, multiple choice....what is the result when user clicks on both markers?

  1. first marker displays 'arg: 0 addr: 1 Smith Street' and second marker displays 'arg: 1 addr: 2 Smith Street'
  2. first marker displays 'arg: 0 addr: 2 Smith Street' and second marker displays 'arg: 1 addr: 2 Smith Street'
  3. first marker displays 'arg: 1 addr: 1 Smith Street' and second marker displays 'arg: 1 addr: 2 Smith Street'
  4. first marker displays 'arg: 1 addr: 2 Smith Street' and second marker displays 'arg: 1 addr: 2 Smith Street'
  5. first marker displays 'arg: undefined addr: undefined' and second marker display 'arg: undefined addr: undefined'

When I run the code, the answer is 3. But I was expecting the answer to be either 4 or 5. Why is it not 4 and why is it not 5?


You seem to be expecting the closure to close over something (which it does), but you're not using anything in particular that it closes over in it (like the loop index). The argnum value you get in your geocodeCallBack comes from Google (because you've defined rownum as the third argument to your anonymous function, shadowing the loop counter), it's nothing to do with your loop counter.

A few other points about that code:

Your loop at the top is looping three times, not two. You have it going from 0 to <= results.length. results.length is 2, and so that will loop with rownum values 0, 1, and 2. You mean < results.length. (You've fixed this.)

There's no purpose to your anonymous function here:

geocoder.geocode( {'address': address[rownum]}, function(results, status, rownum) {geocodeCallBack(results,status,rownum)});

...since all it does is pass on the arguments it receives. That can and probably should just be:

geocoder.geocode( {'address': address[rownum]}, geocodeCallBack);

...unless the signature of the function is wrong and you shouldn't be declaring all of those arguments to it.

Edit: Based on your comments below, I think you may want this:

geocoder.geocode( {'address': address[rownum]}, makeCallback(rownum));

function makeCallback(therow) {
    return function(results, status) {
        geocodeCallBack(results, status, therow);
    };
}

...or in your "real code" as you put it, where you're actually using that row number as part of a selector:

geocoder.geocode( {'address': address[rownum]}, makeCallback(rownum));

function makeCallback(therow) {
    return function(results, status) {
        geocodeCallBack(results, status, $('#row-' + therow).val());
    };
}

The makeCallback function creates a function to be the callback. The function it creates closes over the therow argument passed into makeCallback, which never changes, and so you get therow being 0 for the callback you create on the first loop, and therow being 1 for the callback you create on the second loop.

In terms of understanding closures, I've written up this post which I think you may find helpful: Closures are not complicated It goes into the mechanics of how closures work in some detail. And the title is no lie: They're not complicated, people tend to tie themselves up in knots because they think they're complicated, but they're not.


geocode() is overriding the value of numrow when you declare it as a function parameter.

for(var rownum=0; rownum<=address.length; rownum++)
{
        geocoder.geocode( {'address': address[rownum]}, function(results, status) {geocodeCallBack(results,status,rownum)});
}

Try this instead, this way geocodeCallBack is sent the rownum that is declared in the forloop

The result should be first marker displays 'arg: 0 addr: 1 Smith Street' and second marker displays 'arg: 1 addr: 2 Smith Street'

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜