天天看點

初識js中的閉包

  今天看了關于js閉包方面的文章,還是有些雲裡霧裡,對于一個菜鳥來說,學習閉包确實有一定的難度,不說别的,能夠在網上找到一篇優秀的是那樣的不易。

  當然之是以閉包難了解,個人覺得是基礎知識掌握的不牢,因為閉包牽扯到一些前面的東西,比如作用域\等等,如果連基本的作用域都沒有弄清楚,自然不可能搞懂閉包,還有就是對js的實踐比較少,因為你根本就不知道什麼時候要用這東西,自然談不上對閉包的深刻了解。

  今天我就簡單的說說我目前所了解的閉包,當然可能不完全正确,但是我相信會給你一定的啟發。

  首先我們來談談js中的變量,如果你不知道我為什麼要說這些,那麼你根本沒有掌握js的基礎,建議回頭複習。

js中分:全局變量 和 局部變量

  全局變量:可以在任意位置通路的量就叫全局變量

    

1 var age = 20;
2 function a(){
3     console.log(age); >>20
4 }
5 a();      

  局部變量:函數中用var定義的變量,隻能在函數中通路這個變量,函數外部通路不了。

1 function a(){
2     var age = 20;
3 }
4 a();
5 console.log(age); >> Uncaught ReferenceError: age is not defined      

注意點1:在函數中如果不使用var定義變量那麼js引擎會自動添加成全局變量。

注意點2:全局變量從建立的那一刻起就會一直儲存在記憶體中,除非你關閉這個頁面,局部變量當函數運作完以後就會銷毀這個變量,假如有多次調用這個函數它下一次調用的時候又會重新建立那個變量,既運作完就銷毀,回到最初的狀态,簡單來說局部變量是一次性的,用完就扔,下次要我再重新建立。

函數的相關知識點:

        1. 一個函數内可以嵌套多個函數

    2. 函數裡面的子函數可以通路它上級定義的變量,注意不隻是一級,如果上級沒有會繼續往上級找,直到找到為止,如果找到全局變量到找不到就會報錯。

     

1 function a(){
2     var name = "追夢子";
3     function b(){
4         console.log(name); >> "追夢子"
5     }
6     b();
7 }
8 a();      

    3. 函數的另外一種調用形式,你可以把它叫做自調用,自己調用自己,達到自執行的效果。

   

1 var a = 0;
2 (function(){
3    console.log(++a); >>1
4 })()      

這種方式用()把内容包裹起來,後面的()表示執行這個函數,可能你會問為什麼要把函數包起來,如果不包裹起來,js會把它當作函數聲明來處理,如果包裹起來就是表達式,還沒有看懂就上網查吧。

開始我們正式閉包部分---------------------------- 币包 ---------------像錢包一樣的東西,可以把東西包裹起來----------

      首先我們來看看為什麼需要學習閉包,加以了解 -- 0 v  0- -

1 function a(){
2    var num = 0;
3    console.log(++num);
4 }
5 a(); >>1
6 a(); >>1      

上面代碼輸出了兩次1,為什麼呢?如果你有看我上面的關于變量部分肯定能夠想到個大概。

  前面我們說過了函數執行完以後,裡面的變量(即局部變量)就會銷毀,下一次運作又會重新建立那個變量,是以雖然你第一次++num了但是這個變量在第一次執行完畢以後就被銷毀了。

那麼我們怎麼樣才能確定第一次的變量不被銷毀,那麼就需要我們的閉包出場了。

溫馨提示:JavaScript中有回收機制,函數沒有被引用執行完以後這個函數的作用域就會被銷毀,如果一個函數被其他變量引用,這個函數的作用域将不會被銷毀,(簡單來說就是函數裡面的變量會被儲存下來,你可以了解成全局變量。)

…………………………………………………………………………………… 當 當 當 ................. 下面有請我們的币包同志

function a(){
    var aa = 0;
    function b(){
        aa ++;
        console.log(aa);
    }
    return b;
}
var ab = a();
ab(); //1
ab(); //2      

看到了吧裡面的變量的值沒有被銷毀,因為函數a被外部的變量ab引用,是以變量aa沒有被回收。

     如果某個函數被它的父函數之外的一個變量引用,就形成了一個閉包

還有一種更為常用的閉包寫法

var bi = (function(){
    var a = 0;
    function b(){
        a ++;
        console.log(a);
    }
    return b;
})();

bi(); //1
bi(); //2
bi(); //3      

執行過程分析:

  首先把一個自執行函數指派給了bi,這個自執行函數運作完成以後就bi的值就變成了

function b(){
    a ++;
    console.log(a);
}      

  因為我們在上面的代碼return回去了b,然後因為這個自執行函數被bi引用是以裡面的變量a并沒有因為這個自執行函數執完而銷毀,而是儲存到了記憶體中,是以我們多次列印bi()就成了1、2、3

下面我來說一個閉包的使用場景吧。

   沒有使用閉包的版本

window.onload = function(){
    var ul = document.getElementsByTagName("ul")[0];
    var li = ul.getElementsByTagName("li");
    for(var i=0;i<li.length;i++){
        li[i].onclick = function(){
            console.log(i); //不管我怎麼點都是傳回6
        }
    }
}      

  使用了閉包的版本

window.onload = function(){
    var ul = document.getElementsByTagName("ul")[0];
    var li = ul.getElementsByTagName("li");
    for(var i=0;i<li.length;i++){
        (function(i){
            li[i].onclick = function(){
                console.log(i); //點選第幾個傳回第幾個
            }
        })(i)
    }
}