请解释在循环中使用JavaScript闭包
我已经阅读了一些关于循环内部闭包和闭包的解释。 我很难理解这个概念。 我有这样的代码:是否有办法尽可能地减少代码,这样可以使闭包的概念更清晰。 我很难理解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;
警告:长(ish)答案
这是直接从我在一家公司内部wiki中编写的文章中复制的:
问题:如何在循环中正确使用闭包? 快速回答:使用功能工厂。
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";
所有元素现在将生成一个“你好”的警报! 变量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 */ }
我一直在用JavaScript进行编程很长一段时间,“闭环”是一个非常广泛的话题。 我假设你正在谈论的做法(function(param) { return function(){ ... }; })(param);
在for循环内部,以便在内部函数稍后执行时保留循环的“当前值”。
代码:
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
。
那么在这种情况下关闭的“问题”是,任何对i
访问都会引用相同的变量。 这是因为ECMA-/Javascripts
function scope
或lexical scope
。
所以要避免每一次alert(i);
会显示一个5
(因为循环完成后我=== 5),你需要创建一个新的函数,在运行时自行调用。
为了达到这个目的,你需要创建一个新的函数,并且最后需要额外invoke the outer function
立即invoke the outer function
,所以link.onclick
现在已经把返回的函数作为参考。
闭包是一个构造,在这个构造中,你引用一个变量在它所定义的范围之外。 你通常在函数的上下文中讨论闭包。
var helloFunction; var finished = false; while (!finished) { var message = 'Hello, World!'; helloFunction = function() { alert(message); } finished = true; } helloFunction();
在这里,我定义了变量消息 ,并定义了一个引用消息的函数。 当我定义函数使用消息时,我正在创建一个闭包。 这意味着helloFunction持有对消息的引用,以便我可以继续使用消息 ,甚至在定义消息的作用域(循环体)之外。
附录
(i)在括号中是一个函数调用。 发生什么事情是:
- 你定义了一些函数(num){}。 这被称为匿名函数 ,因为它是内联定义的,并没有名称。
- 函数(num)接受一个整数参数,并返回一个对另一个函数的引用,该函数被定义为alert(num)
- 外部匿名函数立即被调用,参数为i 。 所以num = i 。 这个调用的结果是一个函数,它会提醒(i)。
- 最终的结果或多或少相当于:
link.onclick = function() { alert(i); };
link.onclick = function() { alert(i); };
回答你的问题的最后一部分。 两个括号与其他函数一样调用该函数。 为什么你在这里做的是你想保持变量“我”是在那个时候。 所以它做的是,调用函数,我作为一个参数“num”发送。 由于它被调用,它会记住变量链接自己的勺子的值。
如果你没有这个所有链接点击会导致一个警告说“5”
jQuery创始人John Resig有一个非常好的在线演示解释这个。 http://ejohn.org/apps/learn/
..fredrik