请解释闭包,或将循环计数器绑定到函数范围

Dea*_*ean 7 javascript closures loops function

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

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

有人可以解释一下这背后的逻辑,这个奇怪的语法,我从来没有见过这个:

(function(i))(i);
Run Code Online (Sandbox Code Playgroud)

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

Ano*_*on. 7

(function(i))(i)语法创建一个匿名函数,并立即执行.

通常你会这样做,每次通过循环创建一个新函数,它有自己的变量副本,而不是每个共享同一个变量的事件处理程序.

例如:

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

经常抓住人,因为无论你点击什么按钮,都会doFoo(10)被召唤出来.

鉴于:

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

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


Fel*_*ing 5

这样做是因为JavaScript只有函数作用域,而不是块作用域.因此,每次你在一个循环中声明变量是函数的范围和您创建的每个封闭访问非常相同的变量.

因此,创建新范围的唯一方法是调用函数,这就是什么

(function(i){/* Some code using i */}(i))
Run Code Online (Sandbox Code Playgroud)

是在做.

请注意,您的示例错过了一个重要部分:立即函数必须返回另一个将成为click处理程序的函数:

someArray[i].onclick = (function(i){
    return function() {
       /* Some code using i */
    }
}(i));
Run Code Online (Sandbox Code Playgroud)

直接的功能没什么特别的.它以某种方式内联函数定义和函数调用.您可以通过正常的函数调用替换它:

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

for(var i=0; i < someArray.length; i++){
   someArray[i].onclick = getClickHandler(i);
}
Run Code Online (Sandbox Code Playgroud)