for循环中的闭包
Posted
技术标签:
【中文标题】for循环中的闭包【英文标题】:Closures in a for loop 【发布时间】:2011-01-12 15:16:23 【问题描述】:循环中的闭包给我带来了问题。我想我必须创建另一个函数来返回一个函数来解决问题,但我无法让它与我的 jQuery 代码一起使用。
这是一个简化形式的基本问题:
function foo(val)
alert(val);
for (var i = 0; i < 3; i++)
$('#button'+i).click(function()
foo(i);
);
点击三个按钮中的任何一个都会发出警报,说 3。我想要的功能是点击按钮 1 会发出警报,说 1,按钮 2 会说 2,依此类推。
我怎样才能做到这一点?
【问题讨论】:
【参考方案1】:参见bind 方法。
$('#button'+i).bind('click', button: i, function(event)
foo(event.data.button);
);
来自文档:
可选的 eventData 参数是 不常用。提供时,此 参数允许我们传递额外的 信息给处理者。一个方便的 使用这个参数是工作 围绕关闭引起的问题
【讨论】:
【参考方案2】:试试这个代码:
function foo(val)
alert(val);
var funMaker = function(k)
return function()
foo(k);
;
;
for (var i = 0; i < 3; i++)
$('#button'+i).click(funMaker(i));
这里有一些要点:
javascript 是函数范围的。如果您想要一个新的(“更深”)范围,则需要创建一个函数来保存它。 此解决方案是特定于 Javascript 的,它可以使用或不使用 jQuery。 该解决方案有效,因为i
的每个值都被复制到一个新范围内作为k
,并且从funMaker
返回的函数在k
附近关闭(在循环中不会改变),而不是在附近i
(确实如此)。
您的代码不起作用,因为您传递给click
的函数没有“拥有”i
,它关闭了其创建者的i
,并且i
在循环中发生了变化.
本示例可以使用 funMaker
内联编写,但我通常使用此类辅助函数来使事情更清晰。
funMaker
的参数是k
,但这没什么区别,它本来可以是i
没有任何问题,因为它存在于函数funMaker
的范围内。
在 Sussman & Abelson 的“计算机程序的结构和解释”中可以找到对“环境”评估模型最清晰的解释(http://mitpress.mit.edu/sicp/ 全文在线提供,不易阅读) - 请参阅第 3.2 节。由于 JavaScript 确实是具有 C 语法的 Scheme,因此这种解释是可以的。
编辑:修正了一些标点符号。
【讨论】:
与darkporter的回答相同,但有一些很好的阐述。 “JavaScript 真的是带有 C 语法的 Scheme” 每当有人第一次理解这一点时,一个 JS 天使就会长出翅膀。【参考方案3】:@Andy 解决方案是最好的。但是您也可以使用 Javascript 范围来帮助您保存闭包中的值。
您可以通过执行匿名函数在循环体中创建一个新范围来做到这一点。
for (var i = 0; i < 3; i++)
(function()
var index = i;
$('#button'+index).click(function()
foo(index);
);
)();
由于循环体在每次迭代中都是一个新的作用域,因此索引变量在每次迭代中都会以正确的值复制。
【讨论】:
【参考方案4】:使用 jquery 中的 .each 函数——我猜你是在循环遍历类似的元素——所以使用类似的东西添加点击:
$(element).children(class).each(function(i)
$(this).click(function()
foo(i);
);
);
未经测试,但我总是尽可能使用这种结构。
【讨论】:
【参考方案5】:或者只是制造一个新功能,就像你描述的那样。它看起来像这样:
function foo(val)
return function()
alert(val);
for (var i = 0; i < 3; i++)
$('#button'+i).click(foo(i));
我很确定 Mehrdad 的解决方案行不通。当您看到人们复制到临时变量时,通常是保存“this”的值,该值在内部子范围内可能会有所不同。
【讨论】:
这是我所知道的最好的答案。带有 bind 的代码很好,但是那个 data 参数确实是一个丑陋的 hack。这种方法的优点是它可以在任何时候遇到这个问题(创建引用循环变量的闭包),无论你是否有 jQuery。 @Jason:虽然我同意这是一个很好的答案,当然如果没有在问题中提及和标记 jQuery 我会给出答案,但我不得不不同意它是一个“丑陋的hack”——当然更整洁。此外,第 5 版规范中有一个半相似的Function.bind
,因此您可以说普通 ol' js 的最佳答案是在当前版本的 js 中提供该功能的等效原型设计方法。 snipplr.com/view/13987/functionbind 只是我能找到的一个例子,我知道有很多。
哦,data 参数让我印象深刻,因为它使用 C 风格的穷人闭包来解决实际闭包的问题。这似乎有点悲伤。无论如何,他们都很好,我认为主要问题是他们都有共同的问题:如果没有评论,对于在为什么我们之前没有看过这个特殊问题的读者来说,这永远不够明显'正在跳过这个额外的箍而不是直接关闭i
。 :-\以上是关于for循环中的闭包的主要内容,如果未能解决你的问题,请参考以下文章