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循环中的闭包的主要内容,如果未能解决你的问题,请参考以下文章

for循环中的作用域 闭包

js中闭包for循环

循环中的闭包问题

闭包循环问题-for循环只显示最后一个i的值

带有闭包的 JavaScript For 循环导致 JSLint 警告

for循环,定时器,闭包。