javascript中的闭包个很让人头疼的概念。总结一下
闭包是指有权访问一个函数作用域中的变量的函数。创建闭包最常见的方式,是在一个函数内部创建另一个函数,用return返回出去。
使用闭包可能造成内存占用不足,尽量少使用。
先看几个例子:
1 function foo(){ 2 var a = 2; 3 4 function bar(){ 5 console.log(a); 6 } 7 return bar; 8 } 9 var baz = foo(); 10 baz(); // 2
bar函数就是一个闭包。调用foo()函数时,得到的是bar函数,赋值给baz。此时,baz就指向内部的bar函数。再调用baz函数,就是调用了内部的bar函数了。
1 function foo() { 2 var num = 2; 3 function bar() { 4 alert(++num); 5 } 6 return bar; 7 } 8 var baz = foo();
9 baz(); //3
10 baz(); //4
这和上面的区别是,内部函数变成自加函数,就能说明一些东西了。两次调用,发现它自增,而不是输出相同的数字3,说明了baz函数执行后,num对象没有被销毁,还保存在内存中。
如果没有闭包,那么foo函数执行完后,num对象就要被销毁,但因为闭包的存在,bar函数要访问num对象,所以要把bar函数需要的资源(foo函数)保存在内存中,使其不被销毁。所以输出的是 3 和 4
1 function foo() { 2 var num = 2; 3 function bar() { 4 alert(++num); 5 } 6 bar(); 7 } 8 foo(); //3 9 foo(); //3
如果变成这样,那就没有闭包了(return没了)。无论调用几次都输出3,因为函数运行后就被销毁了,不会保存。
看这个例子:
1 function f1(){ 2 var n=999; 3 nAdd=function(){n+=1} 4 function f2(){ 5 alert(n); 6 } 7 return f2; 8 } 9 var result=f1(); 10 result(); // 999 11 nAdd(); 12 result(); // 1000
要说明的是,nAdd()为什么能在外面调用?有两点
1、没有用var ,是一个全局对象,但单有这个还不够。也不能直接在外面调用
2、f1函数必须先执行才能调用nAdd函数,调用了函数就有了闭包,外面就可以访问里面的全局对象或全局方法了。
看这个例子:
有个全局函数叫setTimeout,可以这样使用:
1 function msg(){ 2 console.log("Message"); 3 } 4 setTimeout(msg,2000);
两秒后输出:Message
下面我们想给msg传递参数,需要让msg返回一个函数给setTimeout使用,如下:
1 function msg(m){ 2 return function() { 3 console.log("Message from: " + m); 4 } 5 } 6 setTimeout(msg("setTimeout"),2000);
两秒后输出:Message from: setTimeout
上面那个是正确版本,msg的参数t作为闭包的一部分绑定给了返回的函数,如果我们这样定义的话就是错误的:
1 function msg(m){ 2 return function(m) { 3 console.log("Message from: " + m); 4 } 5 }
因为返回了一个带参数“m”的函数,会在setTimeout执行的时候在当前上下文中查找一个叫“m”的变量,而并没有此变量,故得到一个非预期的输出如下:Message from: undefined
闭包可以解决一个最常见的循环调用的例子:一个ul,点击每个li,弹出它的索引值。
1 var aLi = document.getElementsByTagName('li'); //假设有6个li 2 for(var i = 0; i < aLi.length; i++){ 3 aLi[i].onclick = function(){ 4 alert(i); 5 } 6 }
看起来好像对了,其实结果是:无论你点击哪一个li,弹出的都是6。这是因为for里面的匿名函数没有保存起来,加载网页的时候执行了,循环结束了。点击时,会去寻找变量i,这时循环结束,i是6。
用闭包可以把它保存起来:
1 var aLi = document.getElementsByTagName('li'); 2 for (var i = 0; i <= aLi.length; i++) { 3 aLi[i].onclick = (function(i){ 4 return function(){ 5 alert(i); 6 }; 7 })(i); 8 }
当点击li时就是调用了闭包函数onclick,使得外部i变量不被销毁,达到目的。
这样也可以解决,通过闭包机制模仿块级作用域
1 var aLi = document.getElementsByTagName('li'); 2 for(var i=0;i<aLi.length;i++){ 3 (function(i){ 4 aLi[i].onclick = function(){ 5 alert(i); 6 } 7 })(i) 8 }
以上为学习总结,如有错误,望指正