为什么数组未定义?

Maa*_*tin 0 javascript google-maps function callback

当我在地理编码器功能之外提醒我的数组"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);
Run Code Online (Sandbox Code Playgroud)

T.J*_*der 7

我怀疑它不是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`
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

现在,回调是对index传递给buildCallback函数的参数的闭包,而不是i主循环中的变量.当我们完成所有回调时,我们callcounter 会发出警报,我们知道这是因为(如果您的"竞争条件"雷达正在关闭,请参阅下面的注释).

所有这一切都是因为闭包的工作方式.它们并不复杂(事实上,我写了一篇关于它们的博客文章,称为闭包并不复杂),但有些事情你需要坚定地理解"得到"为什么他们做他们做的事情.

另外:你正在使用for..in循环opts.markers,我怀疑是一个数组.如果是,则该代码存在您需要解决的问题.for..in是不是通过循环指标的的阵列,它是通过循环属性名称的的对象.更多这里.你需要在你的for..in循环中添加一些检查,或者只是使用一个无聊的老式for循环.


回复counter:对于任何习惯于多线程编程的人来说,我的简单"在调度时递增它,在处理时递减它"逻辑看起来像是设置竞争条件(如果第一个在第二个调度之前被调用回来怎么办? ).但它不是浏览器中JavaScript的竞争条件,因为浏览器上的JavaScript是单线程的(或者如果你使用web worker,那么它是一种多线程协作).这里没有先发制人的多线程.在完成所有调用之前,都不会调用任何回调.


偏离主题:虽然var markerArray = new Array();工作得很好,但我建议var markerArray = [];改为.它更短; 由于各种原因,实现可以更多地优化它(不是真的很重要); 并且不可能有人遮蔽了Array符号.同样,只要您想要一个空白对象,请使用{}而不是new Object().