我已经阅读了关于循环内的闭包和闭包的一些解释。我很难理解这个概念。我有这样的代码:有没有办法尽可能地减少代码,因此闭包的概念可以更清晰。我很难理解i
在两个括号内的部分。感谢
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;
答案 0 :(得分:108)
警告:长(ish)回答
这是直接从我在内部公司维基中写的文章中复制的:
问题:如何在循环中正确使用闭包? 快速回答:使用功能工厂。
for (var i=0; i<10; i++) {
document.getElementById(i).onclick = (function(x){
return function(){
alert(x);
}
})(i);
}
或更容易阅读的版本:
function generateMyHandler (x) {
return function(){
alert(x);
}
}
for (var i=0; i<10; i++) {
document.getElementById(i).onclick = generateMyHandler(i);
}
这经常会让那些不熟悉javascript或函数编程的人感到困惑。这是误解闭包的结果。
闭包不仅传递变量的值,甚至传递变量的引用。闭包捕获变量本身!以下代码说明了这一点:
var message = 'Hello!';
document.getElementById('foo').onclick = function(){alert(message)};
message = 'Goodbye!';
单击元素'foo'将生成一个警告框,其中包含以下消息:“Goodbye!”。因此,在循环中使用简单闭包将最终使所有闭包共享相同的变量,并且该变量将包含在循环中分配给它的最后一个值。例如:
for (var i=0; i<10; i++) {
document.getElementById('something'+i).onclick = function(){alert(i)};
}
点击后的所有元素都会生成一个数字为10的警告框。事实上,如果我们现在i="hello";
,所有元素现在都会生成“hello”警告!变量i在十个函数中共享,与当前函数/范围/上下文相同。可以把它想象成一种私有的全局变量,只有所涉及的函数才能看到。
我们想要的是该变量的实例,或者至少是对变量的简单引用而不是变量本身。幸运的是,javascript已经有了传递引用(对象)或值(对于字符串和数字)的机制:函数参数!
当在javascript中调用函数时,该函数的参数如果是对象则通过引用传递,如果是字符串或数字则按值传递。这足以打破闭包中的变量共享。
所以:
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 */
}
答案 1 :(得分:10)
我已经用JavaScript编程了很长时间,而且“循环闭包”是一个非常广泛的主题。我假设您正在讨论在for循环中使用(function(param) { return function(){ ... }; })(param);
的做法,以便在内部函数稍后执行时保留循环的“当前值”...
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);
}
i==4, x==0
i==4, x==1
i==4, x==2
i==4, x==3
正如您在输出中看到的那样,所有内部回调函数都指向相同的i
,但是,由于每个回调函数都有自己的“闭包”,因此x
的值实际存储就像外部函数执行时的i
一样。
通常,当您看到此模式时,您将使用与参数相同的变量名称和外部函数的参数:(function(i){ })(i)
。该函数内的任何代码(即使稍后执行,如回调函数)在您调用“外部函数”时将引用i
。
答案 2 :(得分:3)
嗯,在这种情况下,闭包的“问题”是,对i
的任何访问都会引用相同的变量。这是因为ECMA-/Javascripts
function scope
或lexical scope
。
所以为了避免每次调用alert(i);
都会显示5
(因为循环完成后我= = 5),你需要创建一个在运行时调用自身的新函数。
要实现这一点,您需要创建一个新函数,并且最后需要额外的paranthesis,立即invoke the outer function
,因此link.onclick
现在已将返回的函数作为参考。
答案 3 :(得分:2)
闭包是一种构造,您可以在该构造中引用其定义范围之外的变量。您通常会在函数的上下文中讨论闭包。
var helloFunction;
var finished = false;
while (!finished) {
var message = 'Hello, World!';
helloFunction = function() {
alert(message);
}
finished = true;
}
helloFunction();
在这里,我定义了变量 message ,并定义了一个引用 message 的函数。当我定义使用消息的函数时,我正在创建一个闭包。这意味着 helloFunction 包含对消息的引用 >,以便我可以继续使用消息,甚至可以在定义消息的范围(循环体)之外。
<强>附录强>
括号中的(i)是函数调用。发生的事情是:
link.onclick = function() { alert(i); };
答案 4 :(得分:0)
回答问题的最后部分。两个括号将该函数作为任何其他函数调用。你为什么这样做是因为你想保留变量“i”就在那个时候。它的作用是,调用函数,i作为参数“num”发送。因为它被调用它将记住变量链接自己的scoop中的值nume。
如果你没有这个全部链接点击将导致提示“5”
jQuery的创始人John Resig有一个非常好的在线演示解释这个。 http://ejohn.org/apps/learn/
..弗雷德里克