请解释 JavaScript 闭包在循环中的使用
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3572480/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Please explain the use of JavaScript closures in loops
提问by CMS scripting
I have read a number of explanations about closures and closures inside loops. I have a hard time understanding the concept. I have this code: Is there a way to reduce the code as much as possible so the concept of closure can be made clearer. I am having a hard time understanding the part in which the iis inside two parenthesis. Thanks
我已经阅读了许多关于闭包和循环内闭包的解释。我很难理解这个概念。我有这个代码:有没有办法尽可能地减少代码,这样闭包的概念就可以更清楚了。我很难理解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;
回答by slebetman
WARNING: Long(ish) Answer
警告:长(ish)答案
This is copied directly from an article I wrote in an internal company wiki:
这是直接从我在公司内部维基上写的一篇文章中复制的:
Question: How to properly use closures in loops? Quick answer: Use a function factory.
问题:如何在循环中正确使用闭包?快速回答:使用函数工厂。
for (var i=0; i<10; i++) {
document.getElementById(i).onclick = (function(x){
return function(){
alert(x);
}
})(i);
}
or the more easily readable version:
或更容易阅读的版本:
function generateMyHandler (x) {
return function(){
alert(x);
}
}
for (var i=0; i<10; i++) {
document.getElementById(i).onclick = generateMyHandler(i);
}
This often confuse people who are new to javascript or functional programming. It is a result of misunderstanding what closures are.
这常常让不熟悉 JavaScript 或函数式编程的人感到困惑。这是误解了什么是闭包的结果。
A closure does not merely pass the value of a variable or even a reference to the variable. A closure captures the variable itself! The following bit of code illustrates this:
闭包不仅仅传递变量的值,甚至是对变量的引用。闭包捕获变量本身!以下代码说明了这一点:
var message = 'Hello!';
document.getElementById('foo').onclick = function(){alert(message)};
message = 'Goodbye!';
Clicking the element 'foo' will generate an alert box with the message: "Goodbye!". Because of this, using a simple closure in a loop will end up with all closures sharing the same variable and that variable will contain the last value assigned to it in the loop. For example:
单击元素“foo”将生成一个带有消息的警告框:“再见!”。因此,在循环中使用一个简单的闭包将导致所有闭包共享相同的变量,并且该变量将包含循环中分配给它的最后一个值。例如:
for (var i=0; i<10; i++) {
document.getElementById('something'+i).onclick = function(){alert(i)};
}
All elements when clicked will generate an alert box with the number 10. In fact, if we now do i="hello";all elements will now generate a "hello" alert! The variable i is shared across ten functions PLUS the current function/scope/context. Think of it as a sort of private global variable that only the functions involved can see.
单击所有元素时将生成一个数字为 10 的警报框。事实上,如果我们现在这样做,i="hello";所有元素现在都会生成一个“你好”警报!变量 i 在十个函数加上当前函数/范围/上下文之间共享。把它想象成一种只有相关函数才能看到的私有全局变量。
What we want is an instance of that variable or at least a simple reference to the variable instead of the variable itself. Fortunately javascript already has a mechanism for passing a reference (for objects) or value (for strings and numbers): function arguments!
我们想要的是该变量的实例,或者至少是对变量的简单引用,而不是变量本身。幸运的是,javascript 已经有一种传递引用(对于对象)或值(对于字符串和数字)的机制:函数参数!
When a function is called in javascript the arguments to that function is passed by reference if it is an object or by value if it is a string or number. This is enough to break variable sharing in closures.
当在 javascript 中调用一个函数时,如果它是一个对象,则该函数的参数通过引用传递,如果它是一个字符串或数字,则通过值传递。这足以打破闭包中的变量共享。
So:
所以:
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 */
}
回答by gnarf
I've been programming in JavaScript for a long time, and "closure in a loop" is a very broad topic. I assume you are talking about the practice of using (function(param) { return function(){ ... }; })(param);inside of a for loop in order to preserve the "current value" of the loop when that inner function later executes...
我已经用 JavaScript 编程很长时间了,“循环闭包”是一个非常广泛的话题。我假设您正在谈论在(function(param) { return function(){ ... }; })(param);for 循环内部使用的做法,以便在稍后执行该内部函数时保留循环的“当前值”...
The code:
编码:
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);
}
Output:
输出:
i==4, x==0
i==4, x==1
i==4, x==2
i==4, x==3
As you can see by the output, all of the inner callback functions all point to the same i, however, since each had its own 'closure', the value of xis actually stored as whatever iwas at the time of the outer function's execution.
正如您在输出中看到的那样,所有内部回调函数都指向相同的i,但是,由于每个函数都有自己的“闭包”,因此 的值x实际上存储为i外部函数执行时的值。
Commonly when you see this pattern, you would use the same variable name as the parameter and the argument to the outer function: (function(i){ })(i)for instance. Any code inside that function (even if executed later, like a callback function) is going to refer to iat the time you called the "outer function".
通常,当您看到此模式时,您会使用与参数和外部函数的实参相同的变量名称:(function(i){ })(i)例如。该函数内的任何代码(即使稍后执行,如回调函数)都将i在您调用“外部函数”时引用。
回答by jAndy
Well, the "problem" with closures in such a case is, that any access to iwould reference the same variable. That is because of ECMA-/Javascriptsfunction scopeor lexical scope.
那么,在这种情况下闭包的“问题”是,任何访问i都会引用相同的变量。那是因为ECMA-/Javascriptsfunction scope或lexical scope。
So to avoid that every call to alert(i);would display a 5(because after the loop finished i === 5), you need to create a new function which invokes itself at runtime.
因此,为了避免每次调用alert(i);都会显示 a 5(因为在循环完成后 i === 5),您需要创建一个在运行时调用自身的新函数。
To achieve this, you need to create a new function, plus you need the extra paranthesis at the end, to invoke the outer functionimmediately, so link.onclickhas now the returned function as reference.
要实现这一点,您需要创建一个新函数,另外还需要在末尾添加额外的括号,toinvoke the outer function立即,因此link.onclick现在将返回的函数作为参考。
回答by RMorrisey
A closure is a construct in which you reference a variable outside the scope in which it's defined. You usually talk about closures in the context of a function.
闭包是一种结构,您可以在其中引用定义它的范围之外的变量。你通常在函数的上下文中谈论闭包。
var helloFunction;
var finished = false;
while (!finished) {
var message = 'Hello, World!';
helloFunction = function() {
alert(message);
}
finished = true;
}
helloFunction();
Here, I define the variable message, and define a function that references message. When I define the function to use message,I am creating a closure.This means helloFunctionholds a reference to message, so that I can continue to use message, even outside of the scope (the loop body) where messageis defined.
在这里,我定义了变量message,并定义了一个引用message的函数。当我定义函数以使用消息时,我正在创建一个闭包。这意味着helloFunction持有对message的引用,以便我可以继续使用message,即使在定义message的范围(循环体)之外。
Addendum
附录
The (i) in parenthesis is a function call. What's happening is:
括号中的 (i) 是一个函数调用。发生的事情是:
- You define some function(num) {}. This is called an anonymous function, because it's defined inline and doesn't have a name.
- function(num) takes an integer argument, and returns a reference to another function, which is defined as alert(num)
- The outer anonymous function is immediately called, with the argument i. So num=i. The result of this call is a function which will do alert(i).
- The end result is more or less equivalent to:
link.onclick = function() { alert(i); };
- 您定义了一些函数(数量){}。这称为匿名函数,因为它是内联定义的并且没有名称。
- function(num) 接受一个整数参数,并返回对另一个函数的引用,该函数定义为 alert(num)
- 使用参数i立即调用外部匿名函数。所以num= i。此调用的结果是一个执行 alert(i) 的函数。
- 最终结果或多或少相当于:
link.onclick = function() { alert(i); };
回答by fredrik
To answer the last part of your questions. The two parenthesis invoke the function as any other functions. Why you do it here is that you want to keep what the variable "i" is just at that time. So what it does is, invoke the function, the i is sent as a argument "num". Since it's invoke it will remember the value nume in variable links own scoop.
回答你问题的最后一部分。两个括号像调用任何其他函数一样调用该函数。你在这里这样做的原因是你想保留当时变量“i”的内容。所以它所做的是,调用函数,将 i 作为参数“num”发送。由于它被调用,它会记住变量链接自己的勺子中的值 nume。
If you did't to this all link click would result in an alert saying "5"
如果您不这样做,所有链接点击都会导致提示“5”
John Resig, founder of jQuery, has a really nice online presentation explaining this. http://ejohn.org/apps/learn/
jQuery 的创始人 John Resig 有一个非常好的在线演示文稿解释了这一点。http://ejohn.org/apps/learn/
..fredrik
..弗雷德里克

