简体   繁体   English

for循环中的闭包

[英]Closures in a for loop

Closures in a loop are causing me problems. 循环中的关闭导致我出现问题。 I think I have to make another function that returns a function to solve the problem, but I can't get it to work with my jQuery code. 我认为我必须制作另一个函数,该函数返回一个函数来解决问题,但是我无法使其与我的jQuery代码一起使用。

Here is the basic problem in a simplified form: 这是简化形式的基本问题:

function foo(val) {
  alert(val);
}

for (var i = 0; i < 3; i++) {
  $('#button'+i).click(function(){
    foo(i);
  });
}

Naturally clicking on any of the three buttons will give an alert saying 3. The functionality I want is that clicking on button 1 will give an alert saying 1, button 2 will say 2 etc. 自然地单击三个按钮中的任何一个都将给出警告说3。我想要的功能是单击按钮1时将给出警告说1,按钮2将说2等。

How can I make it do that? 我该怎么做呢?

See the bind method. 请参见bind方法。

$('#button'+i).bind('click', {button: i}, function(event) {
  foo(event.data.button);
});

From the docs: 从文档:

The optional eventData parameter is not commonly used. 可选的eventData参数不常用。 When provided, this argument allows us to pass additional information to the handler. 提供此参数后,我们便可以将其他信息传递给处理程序。 One handy use of this parameter is to work around issues caused by closures 此参数的一种方便用法是解决由闭包引起的问题

Try this code: 试试这个代码:

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

Some important points here: 这里的一些重要点:

  • JavaScript is function scoped. JavaScript是函数范围的。 If you want a new ('deeper') scope, you need to create a function to hold it. 如果要一个新的(“更深”)作用域,则需要创建一个函数来保存它。
  • This solution is Javascript specific, it works with or without jQuery. 该解决方案是Java特定的,无论是否使用jQuery,它都可以使用。
  • The solution works because each value of i is copied in a new scope as k , and the function returned from funMaker closes around k (which doesn't change in the loop), not around i (which does). 该解决方案之所以有效,是因为i每个值都在新范围内复制为k ,并且funMaker返回的函数在k左右(循环中funMaker关闭,而在i周围(则循环关闭)关闭。
  • Your code doesn't work because the function that you pass to click doesn't 'own' the i , it closes over the i of its creator, and that i changes in the loop. 您的代码无效,因为您传递给click函数不“拥有” i ,它关闭了其创建者的i ,并且i在循环中发生了变化。
  • The example could have been written with funMaker inlined, but I usually use such helper functions to make things clearer. 该示例可能是内联funMaker编写的,但是我通常使用此类帮助器函数使情况更清晰。
  • The argument of funMaker is k , but that makes no difference, it could have been i without any problems, since it exists in the scope of the function funMaker . funMaker的参数是k ,但这没什么区别,因为它存在于funMaker函数的范围之内,所以它可能是i没有任何问题。
  • One of the clearest explanation of the 'Environment' evaluation model is found in 'Structure and Interpretation of Computer Programs', by Sussman & Abelson ( http://mitpress.mit.edu/sicp/ full text available online, not an easy read) - see section 3.2. Sussman&Abelson的“计算机程序的结构和解释”中找到了对“环境”评估模型的最清晰的解释之一( http://mitpress.mit.edu/sicp/全文在线提供,不易阅读) )-请参阅第3.2节。 Since JavaScript is really Scheme with C syntax, that explanation is OK. 由于JavaScript实际上是使用C语法的Scheme,因此可以确定。

EDIT: Fixed some punctuation. 编辑:修复了一些标点符号。

@Andy solution is the nicest. @Andy解决方案是最好的。 But you can also use Javascript scoping to help you save the value in your closure. 但是,您也可以使用Javascript范围定义来帮助您在闭包中保存值。

You do so by creating a new scope in your loop body by executing an anonymous function. 您可以通过执行匿名函数在循环主体中创建新作用域来实现。

for (var i = 0; i < 3; i++) {
  (function(){
    var index = i; 
    $('#button'+index).click(function(){
      foo(index);
    });
  })();
}

Since the loop body is a new scope at each iteration, the index variable is duplicated with the correct value at each iteration. 由于循环主体是每次迭代的新作用域,因此索引变量在每次迭代时都将复制为正确的值。

Use the .each function from jquery - I guess you a looping through similar elements - so add the click using something like: 使用jquery的.each函数-我猜您在类似的元素之间循环-因此请使用类似以下内容添加点击:

$(element).children(class).each(function(i){
   $(this).click(function(){
      foo(i);
   });
});

Not tested but I always use this kind structure where possible. 未经测试,但我总是尽可能使用这种结构。

Or just manufacture a new function, as you describe. 或只是制造一个新功能,如您所描述。 It would look like this: 它看起来像这样:

function foo(val) {
    return function() {
        alert(val);
    }
}

for (var i = 0; i < 3; i++) {
    $('#button'+i).click(foo(i));
}

I'm pretty sure Mehrdad's solution doesn't work. 我很确定Mehrdad的解决方案不起作用。 When you see people copying to a temporary variable, it's usually to save the value of "this" which may be different within an inner child scope. 当您看到人们复制到临时变量时,通常是保存“ this”的值,该值在内部子范围内可能有所不同。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM