2010-04-02 83 views
112

这里的东西简化版本我试图运行:如何将JS变量的值(不是引用)传递给函数?

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的引用?

回答

158

您需要通过将其作为函数参数来创建一个单独的范围,节省的变量在其当前状态:

for (var i = 0; i < results.length; i++) { 
    (function (i) { 
    marker = results[i]; 
    google.maps.event.addListener(marker, 'click', function() { 
     change_selection(i); 
    }); 
    })(i); 
} 

通过创建一个匿名函数与变量调用它作为第一个参数,你正在向函数传递值并创建闭包。

+3

您需要在'marker'之前加'var'来不污染全局命名空间。 – ThiefMaster 2011-03-09 14:48:11

+2

@ThiefMaster:奇怪的是,我刚才在第一次看到这个答案后想到了同样的事情。然而,看着OP的代码,我们不能完全确定'marker'还不是一个全局变量。 – 2011-03-09 14:50:16

+0

已经使用谷歌的地图API,我们可以安全地打赌,标记的作用域在for循环之外。很高兴安迪。 – 2011-04-14 19:28:33

2

你正在关闭关闭。 Here's an article on closures以及如何与他们合作。查看页面上的示例5;这是你正在处理的场景。

编辑:四年后,该链接已死亡。上述问题的根源在于for循环形成了闭包(特别是在marker = results[i])。由于被传入addEventListener,所以您会看到闭包的副作用:共享“环境”在循环的每次迭代中都会更新,最终迭代后最终通过闭包“保存”它。 MDN explains this very well.

12

关闭:

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); 
} 

编辑2013: 这些现在通常被称为IIFE

+0

没有*错误*在这里,但-1,只是因为安迪E先到那里解释更多;这个答案并不会在页面上添加任何内容。 – 2014-12-21 15:20:39

+3

我不确定你了解downvoting的原因。除了Andy的(优秀)答案外,这个答案确实增加了信息:IIFE。 – 2014-12-21 15:33:27

35

还有关闭,您可以使用function.bind

google.maps.event.addListener(marker, 'click', change_selection.bind(null, i)); 

通过价值i我n作为被调用函数的参数。 (null是结合this,你不要在这种情况下需要。)

function.bind由原型框架引入并在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

刚刚注意到了这一点,+1。我非常喜欢'bind',不能等待原生实现的推出。 – 2010-04-18 16:49:44

+0

什么浏览器支持这个?任何移动浏览器? – NoBugs 2012-05-05 07:22:57

+2

@NoBugs:目前:IE9 +。 Fx4 +,最近的Chrome和Opera版本。自从Ice Cream Sandwich以来,没有Safari,没有iPhone,Android浏览器。 – bobince 2012-05-05 19:39:15

-4

我认为,我们可以定义一个临时变量来存储我的价值。

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); 
}); 
} 

虽然我还没有测试过。

+4

不,它不起作用 – newacct 2012-06-07 02:10:24

+5

这不起作用的原因是JavaScript没有块级的范围。所有范围都是功能级别的。你只能通过调用一个函数来创建一个新的范围,这是我们在其他答案中看到的。如果不为循环的每次迭代调用函数,则无法为每个映射事件侦听器回调提供不同的闭包。 这是一个透明的问题,无论何时你使用像'$ .each()'或'_.each()'这样的迭代助手。 – Keen 2013-04-05 11:57:48

-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)); 
} 
+7

如果你解释了它的工作原理,这将是一个更好的答案。 – 2014-05-17 16:04:33