Please explain the use of JavaScript closures in loops
我已经阅读了很多关于循环中闭包和闭包的解释。我很难理解这个概念。我有这段代码:有没有一种方法可以尽可能地减少代码,以便使闭包的概念更加清晰。我很难理解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | function addLinks () { for (var i=0, link; i<5; i++) { link = document.createElement("a"); link.innerHTML ="Link" + i; link.onclick = function (num) { return function () { alert(num); }; }(i); document.body.appendChild(link); } } window.onload = addLinks; |
警告:长(ish)答案
这是直接从我在内部公司wiki上写的一篇文章中复制的:
问题:如何在循环中正确使用闭包?快速回答:使用功能工厂。
1 2 3 4 5 6 7 | for (var i=0; i<10; i++) { document.getElementById(i).onclick = (function(x){ return function(){ alert(x); } })(i); } |
或者更容易阅读的版本:
1 2 3 4 5 6 7 8 9 | function generateMyHandler (x) { return function(){ alert(x); } } for (var i=0; i<10; i++) { document.getElementById(i).onclick = generateMyHandler(i); } |
这常常会让那些不熟悉JavaScript或函数式编程的人感到困惑。这是由于误解了闭包是什么。
闭包不仅仅传递变量的值,甚至传递对变量的引用。一个闭包捕获变量本身!下面的代码说明了这一点:
1 2 3 | var message = 'Hello!'; document.getElementById('foo').onclick = function(){alert(message)}; message = 'Goodbye!'; |
单击元素"foo"将生成一个带有消息"再见"的警告框。。因此,在循环中使用简单的闭包将导致所有闭包共享同一个变量,并且该变量将包含循环中分配给它的最后一个值。例如:
1 2 3 | for (var i=0; i<10; i++) { document.getElementById('something'+i).onclick = function(){alert(i)}; } |
单击时,所有元素都将生成一个数字为10的警报框。实际上,如果现在执行
我们需要的是该变量的一个实例,或者至少是对变量的一个简单引用,而不是变量本身。幸运的是,javascript已经有了一种传递引用(对象)或值(字符串和数字)的机制:函数参数!
在javascript中调用函数时,如果函数是对象,则通过引用传递该函数的参数;如果函数是字符串或数字,则通过值传递。这足以打破闭包中的变量共享。
所以:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | for (var i=0; i<10; i++) { document.getElementById(i).onclick = (function(x){ /* we use this function expression simply as a factory to return the function we really want to use: */ /* we want to return a function reference so we write a function expression*/ return function(){ alert(x); /* x here refers to the argument of the factory function captured by the 'inner' closure */ } /* The brace operators (..) evaluates an expression, in this case this function expression which yields a function reference. */ })(i) /* The function reference generated is then immediately called() where the variable i is passed */ } |
我用JavaScript编程已经很长时间了,"循环中的闭包"是一个非常广泛的主题。我假设您正在讨论在for循环中使用EDOCX1[1]的实践,以便在内部函数稍后执行时保留循环的"当前值"…
代码:1 2 3 4 5 6 7 8 9 10 11 12 13 14 | for(var i=0; i<4; i++) { setTimeout( // argument #1 to setTimeout is a function. // this"outer function" is immediately executed, with `i` as its parameter (function(x) { // the"outer function" returns an"inner function" which now has x=i at the // time the"outer function" was called return function() { console.log("i=="+i+", x=="+x); }; })(i) // execute the"closure" immediately, x=i, returns a"callback" function // finishing up arguments to setTimeout , i*100); } |
输出:
1 2 3 4 | i==4, x==0 i==4, x==1 i==4, x==2 i==4, x==3 |
从输出中可以看到,所有内部回调函数都指向同一个
通常,当您看到这个模式时,您将使用与参数和外部函数的参数相同的变量名:例如
在这种情况下,闭包的"问题"是,任何对
为了避免对
为了实现这一点,您需要创建一个新的函数,再加上在结尾处需要额外的偏执,立即对
闭包是一种构造,在这种构造中,您引用的变量超出了定义它的范围。通常在函数上下文中讨论闭包。
1 2 3 4 5 6 7 8 9 10 11 12 | var helloFunction; var finished = false; while (!finished) { var message = 'Hello, World!'; helloFunction = function() { alert(message); } finished = true; } helloFunction(); |
在这里,我定义变量消息,并定义一个引用消息的函数。当我定义使用消息的函数时,我正在创建一个闭包。这意味着hellofunction持有对消息的引用,这样我就可以继续使用消息,即使在定义消息的作用域(循环体)之外。
补遗
括号中的(i)是一个函数调用。现在发生的是:
回答最后一部分问题。两个圆括号像调用其他函数一样调用该函数。你在这里做这个的原因是你想要保持变量"i"在那个时候是什么。所以它所做的是,调用函数,将i作为参数"num"发送。因为它是被调用的,所以它将记住变量链接中的nume值。
如果您没有链接到所有链接,单击将导致一个警告,提示"5"
jquery的创始人JohnResig有一个非常好的在线演示解释了这一点。http://ejohn.org/apps/learn/
弗雷德里克