for循环中的闭包

IT技术 javascript jquery loops closures
2021-03-02 02:18:47

循环中的闭包给我带来了问题。我想我必须制作另一个返回函数来解决问题的函数,但我无法让它与我的 jQuery 代码一起工作。

这是一个简化形式的基本问题:

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

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

自然地单击三个按钮中的任何一个都会发出警报,说 3。我想要的功能是单击按钮 1 会发出警报,说 1,按钮 2 会说 2,等等。

我怎样才能让它这样做?

5个回答

参见绑定方法。

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

从文档:

可选的 eventData 参数并不常用。提供时,此参数允许我们将附加信息传递给处理程序。此参数的一个方便用途是解决由闭包引起的问题

试试这个代码:

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,并且从funMakercloses around k(在循环中不会改变),而不是 around i在循环中改变)返回的函数
  • 您的代码不起作用,因为您传递给的click函数不“拥有” i,它关闭了i其创建者的 ,并且i在循环发生了变化。
  • 这个例子可以用funMaker内联来编写,但我通常使用这样的辅助函数来使事情更清楚。
  • 的参数funMakeris k,但这没有区别,它本来可以i没有任何问题,因为它存在于 function 的范围内funMaker
  • 在 Sussman & Abelson 所著的“计算机程序的结构和解释”中可以找到对“环境”评估模型最清晰的解释之一(http://mitpress.mit.edu/sicp/全文可在线获取,不容易阅读) - 见第 3.2 节。由于 JavaScript 确实是带有 C 语法的 Scheme,所以这个解释是可以的。

编辑:修正了一些标点符号。

与darkporter的答案相同,但有一些很好的说明。“JavaScript 真的是Scheme with C 语法”每次有人第一次明白这个,JS 天使就长翅膀了。
2021-04-19 02:18:47

@Andy 解决方案是最好的。但是您也可以使用 Javascript 范围来帮助您保存闭包中的值。

您可以通过执行匿名函数在循环体中创建一个新的作用域来实现。

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

由于循环体在每次迭代中都是一个新的作用域,因此索引变量在每次迭代中都被复制为正确的值。

使用 jquery 中的 .each 函数 - 我猜你在循环类似的元素 - 所以使用类似的东西添加点击:

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

未经测试,但我总是尽可能使用这种结构。

或者只是制造一个新功能,正如你所描述的。它看起来像这样:

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

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

我很确定 Mehrdad 的解决方案不起作用。当您看到人们复制到临时变量时,通常是为了保存“this”的值,该值可能在内部子作用域内有所不同。

@Jason:虽然我同意这是一个很好的答案,如果问题中没有提到和标记 jQuery,我当然会给出这个答案,但我不得不不同意它是一个“丑陋的黑客”——它当然更整洁。此外,Function.bind第 5 版规范中有一个半类似的规范,因此您可以说对于普通 ol' js 的最佳答案是等效的原型设计方法,该方法将在当前版本的 js 中提供该功能。 snipplr.com/view/13987/functionbind只是我能找到的一个例子,我知道有很多。
2021-04-20 02:18:47
这是我所知道的最好的答案。带有 bind 的代码很好,但是那个 data 参数真的是一个丑陋的黑客。这种方法的优点是,无论您是否使用 jQuery,只要您遇到此问题(创建引用循环变量的闭包),它都会起作用。
2021-04-28 02:18:47
哦,数据参数让我觉得是一个黑客,因为它使用 C 风格的穷人闭包来解决真正闭包的问题。这似乎有点悲伤。不管怎样,他们都很好,而主要的问题,我认为,这是一个他们都分享到:没有评论,这是从来没有明显不够,以飨读者谁没有见过这方面的问题,为什么通过这个额外的箍我们跳而不是i直接关闭:-\
2021-05-14 02:18:47