在學習閉包之前我們很有必要先了解什么是作用域鏈
一、作用域鏈
作用域鏈是保證對執行環境有權訪問的所有變量和函數的有序訪問。
這句話其實還是蠻抽象的,但是通過下面一個例子,我們就能清楚的了解到作用域鏈了。
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