This question already has answers here:
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
                                
                                    (6个答案)
                                
                        
                2年前关闭。
            
        

当我在地址解析器功能之外向阵列“ markerArray”发出警报时,它表示未定义。
不知道为什么吗?有没有办法从函数外部的数组中获取值?

var markerArray = new Array();
for(var i in opts.markers)
{
    address = opts.markers[i].address;
    //alert(opts.markers[i].icon);
    var geocoder = new google.maps.Geocoder();

    geocoder.geocode({ address: address }, function(results, status) {
        if (status == google.maps.GeocoderStatus.OK && results.length) {
            if (status != google.maps.GeocoderStatus.ZERO_RESULTS) {
                map.setCenter(results[0].geometry.location);
                var marker = new google.maps.Marker({
                    position: results[0].geometry.location,
                    map: map
                });
            }
        }
        markerArray[i] = marker;

    });

}
alert(markerArray[0].position);

最佳答案

我怀疑它不是抱怨的markerArray,而是markerArray[0]不确定的。

您正在使用在循环中创建的函数来调用异步API。这些功能是闭包。它们每个都有对i变量的持久引用,而不是定义函数时的值副本。因此,所有函数都使用循环中的最后一个i值,因为直到循环结束,它们都不会运行。因此,如果循环中i的最后一个值是5,则所有函数都将使用5

另外,在任何回调都有机会运行之前,您过早地执行alert。您将需要在回调之一中完成您需要做的所有最终处理(您可以使用计数器来知道它们何时全部发生)。

您可以像这样解决markerArray问题和过早的alert

var markerArray = new Array();
var callcounter = 0;
for(var i in opts.markers)
{
    address = opts.markers[i].address;
    //alert(opts.markers[i].icon);
    var geocoder = new google.maps.Geocoder();

    ++callcounter;
    geocoder.geocode({ address: address }, buildCallback(i));

}

function buildCallback(index) {
    return function(results, status) {
        if (status == google.maps.GeocoderStatus.OK && results.length) {
            if (status != google.maps.GeocoderStatus.ZERO_RESULTS) {
                map.setCenter(results[0].geometry.location);
                var marker = new google.maps.Marker({
                    position: results[0].geometry.location,
                    map: map
                });
            }
        }
        markerArray[index] = marker;
        if (--callcounter === 0) {
            // This was the last outstanding call
            alert(markerArray[0]); // Always assuming there was a `0` in `opts.markers`
        }
    };
}


现在,回调是传递给index函数的buildCallback参数的闭包,而不是主循环中的i变量。当我们完成所有回调后,我们会发出警报,由于callcounter而使我们知道(如果您的“竞赛条件”雷达即将关闭,请参阅以下注释)。

所有这些都是由于闭包的工作方式。它们并不复杂(实际上,我写了一篇有关Closures are not complicated的博客文章),但是您需要牢牢理解一些事情,以“了解”他们为什么要做自己的事情。

另外:您正在使用for..in遍历opts.markers,我怀疑这是一个数组。如果是这样,则该代码存在您需要解决的问题。 for..in不是用于遍历数组的索引,而是用于遍历对象的属性名称。 More here.您需要在for..in循环中添加一些检查,或者仅使用无聊的for循环。



counter:对于习惯多线程编程的任何人,我简单的“在调度时增加它,在处理时减少它”逻辑看起来像设置了竞争条件(如果第一个在第二个之前被回调预定?)。但这不是浏览器上JavaScript的竞争条件,因为浏览器上的JavaScript是单线程的(或者,如果使用web workers,则是协作多线程的)。这里没有抢占式多线程。在计划好所有回调之前,将不会调用这些回调。



题外话:尽管var markerArray = new Array();可以正常工作,但我还是建议使用var markerArray = [];。它更短;由于种种原因,实现可以对其进行更多优化(这并不重要);而且不可能有人遮盖了Array符号。同样,任何时候只要您只需要一个空白对象,就使用{}代替new Object()

关于javascript - 为什么数组未定义? ,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/6211777/

10-11 06:55