2

我试图将一个数组传递给一个函数,然后使用该数组中的信息来初始化一个谷歌地图。但是,当我单击地图上的标记时,会产生一个错误,上面写着:

无法获取属性“popupHtml”的值:对象为空或未定义

创建该函数的原因是可以将 javascript 代码移动到单独的 .js 文件中,从而与 html 文件分开。无论如何,这个问题可以纠正吗?这是我的所有代码(我在其中添加了注释以标记错误发生的位置......):

<!DOCTYPE html>

<html>
<head>    
    <script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script>
    <script type="text/javascript">
        window.onload = function ()
        {
            function loadMap(markers)
            {
                var options =
                {
                    center: new google.maps.LatLng(40.775813, -73.970786),
                    zoom: 17,
                    mapTypeId: google.maps.MapTypeId.ROADMAP
                };
                var map = new google.maps.Map(document.getElementById('map'), options);

                var points = new Array();
                for (var i = 0; i < markers.length; i++)
                    points.push(new google.maps.LatLng(markers[i].lat, markers[i].lon));
                var infoWindow = new google.maps.InfoWindow();
                for (var i = 0; i < markers.length; i++)
                {
                    var googleMarker = new google.maps.Marker({ position: points[i], map: map, title: markers[i].title });
                    google.maps.event.addListener(
                        googleMarker,
                        'click',
                        function ()
                        {
                            // The following line is where the error is occuring:
                            infoWindow.setContent(markers[i].popupHtml);
                            infoWindow.open(map, googleMarker);
                        });
                }
            };

            loadMap([{
                "lat": "40.776512",
                "lon": "-73.970293",
                "popupHtml": "\u003cdiv\u003eHello world - from marker 1!\u003c/div\u003e",
                "title": "Marker 1!"
            },
            {
                "lat": "40.774659",
                "lon": "-73.971548",
                "popupHtml": "\u003cdiv\u003eHello world - from marker 2!\u003c/div\u003e",
                "title": "Marker 2!"
            }]);
        };
    </script>
</head>
<body>
    <div id="map" style="width: 680px; height: 400px;"></div>
</body>
</html>
4

4 回答 4

3

问题是您传入的函数具有对变量addListener的持久访问权限,而不是创建函数时其值的副本。因此,函数的所有副本都在它们被调用时看到,这可能超过了数组的末尾。也一样;他们都会看到它在循环中的最后一个值,而不是当前值。iigoogleMarker

您可以使用生成器函数来修复它,或者(如果您可以依赖 ECMAScript5,或者如果您使用 ES5 shim,因为bind这是 shim 可以提供的东西)使用Function#bind.

使用bind

for (var i = 0; i < markers.length; i++)
{
    var googleMarker = new google.maps.Marker({ position: points[i], map: map, title: markers[i].title });
    google.maps.event.addListener(
        googleMarker,
        'click',
        (function (index, thisMarker)
        {
            // The following line is where the error is occuring:
            infoWindow.setContent(markers[index].popupHtml);
            infoWindow.open(map, thisMarker);
        }).bind(undefined, i, googleMarker)
    );
}

bind返回一个函数,该函数在调用时将使用给定this值和您提供的参数调用原始函数。所以在上面,我们调用bind传入 and 的值它给了我们一个函数,当被调用时,它将以这些值作为参数调用我们的原始函数。然后我们使用参数 ( and ) 而不是and 。igoogleMarkerindexthisMarkerigoogleMarker

如果您不能在目标浏览器中依赖 ES5 功能并且不想使用 shim,则可以使用生成器函数:

for (var i = 0; i < markers.length; i++)
{
    var googleMarker = new google.maps.Marker({ position: points[i], map: map, title: markers[i].title });
    google.maps.event.addListener(
        googleMarker,
        'click',
        makeHandler(i, googleMarker)
    );
}

function makeHandler(index, thisMarker) {
    return function ()
    {
        // The following line is where the error is occuring:
        infoWindow.setContent(markers[index].popupHtml);
        infoWindow.open(map, thisMarker);
    };
}

我们在这里调用makeHandler,传入 and 的值并返回一个函数,该函数关闭这些参数 ( and ) 而不是and 。由于参数不会改变,我们的函数将看到正确的值。igoogleMarkermakeHandlerindexthisMarkerigoogleMarkermakeHandler

这一切都与闭包的工作方式有关。关于闭包的更多信息:闭包并不复杂

于 2012-05-01T08:59:20.643 回答
0

您对 loadMap 函数的定义在 window.onload 处理程序中,因此在页面加载之前不可用,即使该函数处于不同的范围内。

我认为你的事情有点倒退 - 我将 loadMap 函数的定义与对 loadMap 的调用交换,以便调用在 onload 事件中,而定义不在。

于 2012-05-01T09:02:16.033 回答
0

正如 TJ Crowder 所指出的i,即使在闭包内,价值也会随着循环的进行而变化。除了他的回答之外,解决此问题的一种简单方法是在循环范围内创建一个变量。

for (var i = 0; i < markers.length; i++)
    {
        var marker = markers[i]; // new variable is created for each loop iteration
        var googleMarker = new google.maps.Marker({ position: points[i], map: map, title: marker.title });
        google.maps.event.addListener(
            googleMarker,
            'click',
            function ()
            {
                // The following line is where the error is occuring:
                infoWindow.setContent(marker.popupHtml);
                infoWindow.open(map, googleMarker);
            });
    }
于 2012-05-01T09:06:51.697 回答
0

您可以使用简单的匿名函数,因此在这里您执行一个函数,该执行的上下文将保存您的步长值。

for (var i = 0; i < markers.length; i++)
{
    var googleMarker = new google.maps.Marker({ position: points[i], map: map, title: marker.title });

    (function(step){google.maps.event.addListener(
        googleMarker,
        'click',
        function ()
        {
            // The following line is where the error is occuring:
            infoWindow.setContent(markers[step].popupHtml);
            infoWindow.open(map, googleMarker);
        });
    )(i);
}
于 2012-05-01T09:21:08.540 回答