【问题标题】:Please explain closures, or binding the loop counter to the function scope请解释闭包,或将循环计数器绑定到函数范围
【发布时间】:2011-01-23 21:52:00
【问题描述】:

我见过程序员使用计数器在循环内分配事件侦听器。我相信这是语法:

for(var i=0; i < someArray.length; i++){
   someArray[i].onclick = (function(i){/* Some code using i */})(i);
}

谁能解释一下这背后的逻辑,以及这种奇怪的语法,我从未见过:

(function(i))(i);

非常感谢您的时间和耐心。

【问题讨论】:

  • 这在技术上不涉及关闭。虽然我不完全确定它有什么好处;该内部函数将立即运行,除非它返回另一个函数,否则 onclick 将设置为非常奇怪的值。
  • @cHao 因为 this,立即匿名函数调用,被分配给 onlick,它必须返回一个函数,所以返回的函数是一个在周围环境中带有“i”变量的闭包。
  • @jcubic:如果代码确实设置了 DOM 事件处理程序,它必须返回一个函数如果它工作正常。这里没有足够的代码来证明任何一个假设。

标签: javascript function loops closures


【解决方案1】:

(function(i))(i) 语法创建一个匿名函数,然后立即执行它。

通常,您会在每次循环中创建一个新函数,该函数拥有自己的变量副本,而不是每个事件处理程序共享同一个变量。

例如:

for(int i = 0; i < 10; i++)
    buttons[i].click = function() { doFoo(i); };

经常抓人,因为无论你点击什么按钮,doFoo(10) 都会被调用。

鉴于:

for(int i = 0; i < 10; i++)
    buttons[i].click = (function(i){ return function() { doFoo(i); };)(i);

为每次迭代创建一个内部函数的新实例(具有自己的值i),并按预期工作。

【讨论】:

  • 所以你在匿名函数中插入你想要分配给特定事件的函数,并且此时“计数器”的值保留在新函数范围内?
  • @Dean:我已将一些代码示例编辑到我的答案中。他们有帮助吗?
  • @Dean 是的,它可以工作,因为参数在传递给函数(对于原语)时被复制
  • @Anon,是的,谢谢,看起来很清楚。你在插入的函数前面输入了“return”,如果我把“return”留在外面,它会给我一个错误。立即执行的匿名函数“返回”您插入其中的函数?
  • @Dean:函数是 JS 中的一等公民。所以是的,它只是返回这个内部函数。
【解决方案2】:

这样做是因为 JavaScript 只有函数作用域,没有块作用域。因此,您在循环中声明的每个变量都在函数的范围内,并且您创建的每个闭包都可以访问相同的变量。

所以创建新作用域的唯一方法是调用一个函数

(function(i){/* Some code using i */}(i))

正在做。

请注意,您的示例遗漏了一个重要部分:立即函数必须返回另一个函数,该函数将是 click 处理程序:

someArray[i].onclick = (function(i){
    return function() {
       /* Some code using i */
    }
}(i));

立即函数没有什么特别之处。它以某种方式内联函数定义和函数调用。您可以将其替换为普通函数调用:

function getClickHandler(i) {
    return function() {
         /* Some code using i */
    }
}

for(var i=0; i < someArray.length; i++){
   someArray[i].onclick = getClickHandler(i);
}

【讨论】:

  • +1 表示getClickHandler 版本,这是一种更好的 IMO 方法。
猜你喜欢
  • 2021-04-16
  • 1970-01-01
  • 2013-01-23
  • 1970-01-01
  • 2018-11-04
  • 1970-01-01
  • 1970-01-01
  • 2018-09-15
  • 1970-01-01
相关资源
最近更新 更多