关于Javascript闭包closure的应用

最近在研究百度地图API的应用,试验了iOS SDK后又开始试验Web端的JS版应用,做到在地图上通过Ajax异步调用方式从后端取得数据后,添加标注并给标注添加对应的点击时间alert出此位置的信息时遇到了一个Javascript问题,最开始的code是这样写的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$.ajax({
    type : "get",
    url : urlStr,
    async : true,
    dataType : "json",
    error : function() {
        alert('No valid location record!');
    },
    success : function(json) {
        for(var i=0;i<json.length;i++)
        {
            var point = new BMap.Point(json[i].longtitude, json[i].latitude);
            var marker = new BMap.Marker(point);        // 创建标注 
                                // 添加点击事件侦听
            mark.addEventListener("click", function()
                    {
                    alert('记录时间: '+json[i].logTime+' 定位时间: '+json[i].gpsTime);
                    return;
                    });
            map.addOverlay(marker);                     // 将标注添加到地图中
        }
        var point = new BMap.Point(json[0].longtitude, json[0].latitude); 
        map.centerAndZoom(point, 15);
    }
    });

这样运行时,点击任意一个标注后就会收到浏览器的js脚本错误,提示json[i].logTime还有后面同样的gpsTime部分有问题,检查了一下,发现Ajax请求成功后的函数内循环遍历数据时的i是局部变量,而每一个mark的click侦听函数内的i都为对同一个变量的引用,也就是说循环遍历后每次点击触发执行这段响应代码时的i都是同一个数,而不是认为的对应每一个mark时的i,也自然就取不到所期望的数据了!

解决这个问题的方法就是利用JS闭包特性,将需要保留每一次循环时的值通过一次函数调用封进函数体内,保持作用时间,修改后的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function addClickHandler(mark, logTime, gpsTime) {
    mark.addEventListener("click", function()
    {
        alert('记录时间: '+logTime+' 定位时间: '+gpsTime);
        return;
    });
}
 
$.ajax({
    type : "get",
    url : urlStr,
    async : true,
    dataType : "json",
    error : function() {
        alert('No valid location record!');
    },
    success : function(json) {
        for(var i=0;i<json.length;i++)
        {
            var point = new BMap.Point(json[i].longtitude, json[i].latitude);
            var marker = new BMap.Marker(point);        // 创建标注 
            addClickHandler(marker, json[i].logTime, json[i].gpsTime);
            map.addOverlay(marker);                     // 将标注添加到地图中
        }
        var point = new BMap.Point(json[0].longtitude, json[0].latitude); 
        map.centerAndZoom(point, 15);
    }
    });

这样,通过addClickHandler将循环时每一次添加mark时对应此位置的信息通过函数参数传递进函数体内并作为click中alert显示信息的变量,这样click会在后面执行到,而需要的信息会为此mark一直保留。

参考文章:

  • Callbacks in Loops http://tobyho.com/2011/11/02/callbacks-in-loops/

    博主友情提示:

    如您在评论中需要提及如QQ号、电子邮件地址或其他隐私敏感信息,欢迎使用>>博主专用加密工具v3<<处理后发布,原文只有博主可以看到。