在学习闭包之前我们很有必要先了解什么是作用域链
一、作用域链
作用域链是保证对执行环境有权访问的所有变量和函数的有序访问。
这句话其实还是蛮抽象的,但是通过下面一个例子,我们就能清楚的了解到作用域链了。
1 var color="blue"; 2 function changeColor(){ 3 var anotherColor="red"; 4 function swapColors(){ 5 var tempColor=anotherColor; 6 anotherColor=color; 7 color=tempColor; 8 //这里面可以访问color、anotherColor和tempColor 9 } 10 //这里面可以访问anotherColor和color 11 } 12 //这里面只能访问color
以上代码涉及了3个执行环境:全局环境、changeColor()局部环境和swapColor()局部环境。在一个变量环境中只能访问他自己的环境和父执行环境。swapColor()的父执行环境就是changeColor(),而changeColor()的父执行环境就是全局环境。如果还不清楚可以参考下图
在f2()函数的局部环境中能访问自己和比它等级高的,也就是a,b,c,同理f1()函数环境访问b,a,全局环境只能访问c。
我们这时候会发现函数外部是无法访问内部的局部环境的,但是我们想突破作用域链怎么办呢?这时候就有了闭包共享作用域。闭包这个名词就出现了。
二、闭包
闭包在红宝书中的解释就是:有权访问另一个函数作用域中的变量的函数。
下来举一个简单的例子
1 function f1(){ 2 var a=1; 3 return function(){ 4 return a; 5 } 6 } 7 alert(a); /*结果为 a is undefined*/ 8 var task=f1(); /*task就是闭包,有权访问其他函数作用域变量*/ 9 alert(task());/*结果为1*/
下来我们再举一个闭包的例子
1 function f1(){ 2 var n=0; 3 task=function(){ //匿名函数 4 n+=1; 5 } 6 //这部分为闭包 7 function f2(){ 8 alert(n); 9 } 10 return f2 //返回 12 } 13 var text=f1(); 14 alert(text()); 15 task(); 16 alert(text()); //结果依次为 0,undefined,1,undefined
text是f2闭包函数,实际上f2()被赋予一个全局变量,f2()始终在内存中,f1()是它的父级函数,所以f1()也始终在内存中,不会被销毁。执行一次task()后,值变为2。至于为什么会出现undefined是因为,undefined是text的返回值,也就是闭包函数无返回值了。
有时候我们想改变其他函数作用域里的变量,这时候就可以用闭包去解决。设置两个额外的函数去访问内部函数。
1 var setvalue,getvalue; 2 (function(){ 3 var n=0; 4 getvalue=function(){ 5 return n 6 } 7 setvalue=function(x){ 8 n=x; 9 } 10 })(); //直接调用 11 alert(getvalue()); //结果为0 12 setvalue(456); 13 alert(getvalue());/*结果为456*/
这时候我们发现在外部就可以去改变内部变量值。
下来再举一个利用闭包循环遍历得到数组的值。有两个程序,可以对比一下两个程序的区别
例1:
1 function f1(){ 2 var a=[]; 3 var i=0; 4 for(i=0;i<3;i++){ 5 a[i]=function(){ 6 return i; 7 } 8 } 9 return a; 10 } 11 var text=f1(); 12 alert(text[0]()); 13 alert(text[1]()); 14 alert(text[2]()); //结果都为3
因为闭包都指向局部变量i,只是给出了指针链接,对变量的引用,并没有对值作出改变。所以结果都为3
例2:
1 function f1(){ 2 function f2(x){ //闭包 3 return function(){ 4 return x; 5 } 6 } 7 var a=[]; 8 var i=0; 9 for(i=0;i<3;i++){ 10 a[i]=f2(i); 11 } 12 return a; 13 } 14 var text=f1(); 15 alert(text[0]()); //结果为1 16 alert(text[1]());//结果为2 17 alert(text[2]());//结果为3
利用一个函数参数,用闭包去获取内部变量的值。
程序是这样走的:先判断谁进来--》调用闭包--》闭包返回内部函数参数--》最后再创建数组。
三、闭包的缺点
这上面几个例子确实体会到了闭包的强大,但是闭包也有明显的缺点,它使函数中的变量都保存在内存中,占用内存,导致页面加载缓慢。所以再退出函数前,将不用的局部变量删除。
四、总结
以上就是我学习闭包总结的一点小知识。大家互相交流哈 O(∩_∩)O。
附阮一峰老师对闭包的理解:http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html?20120612141317#comments