以下是我正在尝试运行的简化版本:
for (var i = 0; i < results.length; i++) {
marker = results[i];
google.maps.event.addListener(marker, 'click', function() {
change_selection(i);
});
}
但我发现每个监听器都使用results.length的值(for循环终止时的值)。我如何添加监听器,使得每次使用i时我添加它的值,而不是对i的引用?
答案 0 :(得分:161)
在现代浏览器中,您可以使用let
或const
关键字创建块范围变量:
for (let i = 0; i < results.length; i++) {
let marker = results[i];
google.maps.event.addListener(marker, 'click', () => change_selection(i));
}
在旧版浏览器中,您需要创建一个单独的作用域,通过将变量作为函数参数传递,将变量保存在当前状态:
for (var i = 0; i < results.length; i++) {
(function (i) {
marker = results[i];
google.maps.event.addListener(marker, 'click', function() {
change_selection(i);
});
})(i);
}
通过创建一个匿名函数并使用变量作为第一个参数调用它,您将按值传递给函数并创建一个闭包。
答案 1 :(得分:35)
除了闭包,您还可以使用function.bind
:
google.maps.event.addListener(marker, 'click', change_selection.bind(null, i));
调用时,将i
in的值作为参数传递给函数。 (null
用于绑定this
,在这种情况下您不需要。)
function.bind
由Prototype框架引入,并已在ECMAScript第五版中标准化。在浏览器本身都支持它之前,您可以使用闭包添加自己的function.bind
支持:
if (!('bind' in Function.prototype)) {
Function.prototype.bind= function(owner) {
var that= this;
var args= Array.prototype.slice.call(arguments, 1);
return function() {
return that.apply(owner,
args.length===0? arguments : arguments.length===0? args :
args.concat(Array.prototype.slice.call(arguments, 0))
);
};
};
}
答案 2 :(得分:13)
封:
for (var i = 0, l= results.length; i < l; i++) {
marker = results[i];
(function(index){
google.maps.event.addListener(marker, 'click', function() {
change_selection(index);
});
})(i);
}
EDIT,2013: 现在这些通常被称为IIFE
答案 3 :(得分:2)
你闭嘴了。 Here's an article on closures以及如何使用它们。查看页面上的示例5;这就是你正在处理的情景。
for
循环形成闭包(特别是marker = results[i]
)。当marker
被传递到addEventListener
时,你会看到闭包的副作用:共享的“环境”随着循环的每次迭代而更新,然后在最后一次通过闭包“保存”之后迭代。 MDN explains this very well.
答案 4 :(得分:-2)
for (var i = 0; i < results.length; i++) {
marker = results[i];
google.maps.event.addListener(marker, 'click', (function(i) {
return function(){
change_selection(i);
}
})(i));
}
答案 5 :(得分:-4)
我认为我们可以定义一个临时变量来存储i的值。
for (var i = 0; i < results.length; i++) {
var marker = results[i];
var j = i;
google.maps.event.addListener(marker, 'click', function() {
change_selection(j);
});
}
我还没有测试过它。