帶你徹底瞭解閉包以及其原理

閉包一直是許多初學者的難題,網上對閉包的講解也是衆說紛紜,但還是許多人不能明白。

下面我通過五個簡單例子,讓你明白閉包原理。

第一個例子

<script>        
    var i = 0;
    document.onclick = addNumber;
    function addNumber(){
        i++;
        document.title =i;
    };
</script>

第一個例子都能看懂,是個簡簡單單的實現 i 累加。無需多說,我們繼續看第二個例子。

第二個例子

<script>         
document.onclick = addNumber;
    function addNumber(){
        var i = 0;
        i++;
        document.title =i; 
    };
</script>

第二個例子:我們會發現 i 這時候無法實現累加,一直顯示是1。這是爲什麼?

原因是:

1、第一個例子的 i 定義在函數外面,它在一個叫做"全局作用域"的區域裏面。"全局作用域"只會在瀏覽器窗口關閉或頁面刷新的時候進行關閉。(我們跟第二個例子對比下就知道全局作用域)。

2、第二個例子的 i 定義在函數裏面,它在一個叫做"局部作用域"的區域裏面。(局部作用域是我自己命名的,方便理解)。"局部作用域"會在函數被調用的時候創建,而在函數運行結束的時候關閉(並且裏面創建的變量跟函數也會被刪掉–垃圾回收機制)。

3、通過上面的說法:我們就知道了,第一個例子能實現累加,是因爲 i 一直儲存在全局作用域,沒有被刪掉。第二個例子,我們調用了函數,然後創建了局部作用域->定義了 i ->函數運行結束後->局部作用域關閉了,i 也被刪除了。然後我們再點擊, 又重新創建了局部作用域,重新定義 i 。(我們點擊是在不停重新創建 i)

問題:有沒有辦法讓“局部作用域”不關掉呢?那就是"閉包"了。

第三個例子:簡單的閉包

<script>        
document.onclick =addNumber1();
 
function addNumber1(){   
    var i = 0;
    function addNumber2() {
    i++;
    document.title =i;
   }
   return addNumber2;        //運行addNumber1()的獲得的值是addNumber2
}
</script>

第三個例子: 這次還是將 i 定義到函數裏面,但我們會發現,i 實現了累加。 這說明我們已經讓局部作用域不關閉了,這就是簡單的閉包。

我們來分析爲什麼局部作用域沒有被關閉:

①我們在函數內部定義了 i

②定義了函數 addNumber2(),並且在addNumber2()引用了 i

③最後return addNumber2。

④(要注意的是:這裏document.οnclick=addNumber1(); 並不是調用addNumber1函數[調用是不加括號],而是讓addNumber1()執行了,獲取到了addNumber1() 的值)。 其實相當於 documen.onclick = addNumber2 //不能直接這麼寫,爲了方便理解,我們假設它存在 ;

⑤ 這時就會神奇的發現,我們竟然可以在全局環境中使用在函數裏面定義的函數。(一般情況下,全局環境無法使用函數裏面定義的變量,就是你在函數外面無法使用函數裏面定義的變量,[函數裏面定義的變量同理])。

⑥ 因爲documen.onclick = addNumber2;的存在(作用域鏈的存在,我會在另一篇文章中分析作用域鏈),addNumber2的執行依賴addNumber1()裏面定義的 i,使得addNumber1()不會被關閉,i 也不會被重新定義,累加的值也被成功的保存下來。

第四個例子:簡寫閉包

<script>         
document.onclick =(function(){ //匿名函數自執行
    var i = 0; 
    return function() {       //返回另一個匿名函數
        i++; 
        document.title =i; 
    } 
})();
</script>

第四個例子:這裏涉及到 “匿名函數自執行” 。(function(){})(); 一般執行函數是在函數名後面加括號,這裏(function(){})相當於一個表達式,我們在它後面加(); 。就相當於執行了這個函數。 同樣的,我們 return function(){}; 。 也是把這個函數返回出去了,只不過返回出去一個匿名函數。

第五個例子:閉包的作用。

<script>    
var i = 10 ;     
document.onclick =(function(){ 
    var i = 0; 
    return function() {       
        i++; 
        document.title =i; 
    } 
})();
console.log(i);
</script>

第五個例子:有蠻多初學者說,都聽別人閉包好用,可是不知道他的實際應用。像第五個例子,我在全局也定義了一個 i ,也對它進行了控制檯輸出,發現我們閉包裏的函數並不會對全局造成影響,卻實現了全局的效果。對於比較大型的合作項目,很多人分工合作,如果每個人都在全局定義變量 ,如果定義了同樣名字的變量,對這兩個人的程序都會造成一些影響。 這就是閉包。

閉包的用處還很多,明白了原理,你可以繼續挖掘它。

還有點需要注意的是:閉包的存在會使得他不會被垃圾回收機制回收,他會比其他函數佔更多的內存,過渡使用閉包可能會導致內存佔用過多。可能會導致瀏覽器崩潰的問題。解決方法就是已經達到我們的目的的時候,解除對匿名函數的引用,沒有了引用變成了普通函數,被垃圾回收機制回收。

發佈了154 篇原創文章 · 獲贊 167 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章