javascript閉包入門理解(非常詳細)

來源:互聯網
上載者:User

之所以再寫一篇閉包的文章,原因有二:一個是之前的文章寫的不是很清楚,比較含糊。之前文章請看js閉包文章一 。原因二是閉包確實是js的痛點和重點,不少網友也問過我,所以,決定再寫一篇關於閉包的文章。

案例

首先看下閉包的一個簡單案例

function A(){
    function B(){
       console.log("Hello haorooms!");
    }
    return B;
}
var c = A();
c();//Hello haorooms!
這個例子是一個比較簡單的閉包,解釋如下:

(1)定義了一個普通函數A
(2)在A中定義了普通函數B
(3)在A中返回B(確切的講,在A中返回B的引用)
(4)執行A(),把A的返回結果賦值給變數 c
(5)執行 c()
當一個內建函式被其外部函數之外的變數引用時,就形成了一個閉包。

閉包的作用

在上述例子中,B定義在A中,因此B依賴於A,而外部變數 c 又引用了B, 所以A間接的被 c 引用,也就是說,A不會被回收,會一直儲存在記憶體中。為了證明我們的推理,上面的例子稍作改進。也可以看我之前閉包文章中的例子:。

function A(){
    var count = 0;
    function B(){
       count ++;
       console.log(count);
    }
    return B;
}
var c = A();
c();// 1
c();// 2
c();// 3
count是A中的一個變數,它的值在B中被改變,函數B每執行一次,count的值就在原來的基礎上累加1。因此,A中的count一直儲存在記憶體中。

這就是閉包的作用,有時候我們需要一個模組中定義這樣一個變數:希望這個變數一直儲存在記憶體中但又不會“汙染”全域的變數,這個時候,我們就可以用閉包來定義這個模組。

常見的閉包寫法

前面我的文章中多次提及“自調用匿名函數”,


包裹方法如下:

(function(){

})()
用上面的這個包裹起來,就可以了。

但是還有一個問題,當我們研究大牛的代碼的時候,前面經常看到有“;”,那是為了避免代碼合并等不必要的錯誤。

例如,我們隨便定義一個函數:

var haoroomsblog=function(){

}
(function(){

    })()
由於haoroomsblog這個函數後面沒有加分號,導致代碼出錯,為了避免這類情況的發生,通常這麼寫!

;(function(){

    })()
把你的外掛程式程式碼封裝裹在上面裡面,就是一個簡單的外掛程式了。(注js外掛程式和jquery外掛程式都是如此)

還有一個問題

把你的外掛程式包裹在

;(function(){

    })()
基本上可以說是完美了。但是為了讓你開發的外掛程式應用更加廣泛,相容性更加好,還要考慮到用外掛程式的人的一些特殊的做法,例如,有些朋友為了避免jquery和zeptojs衝突,將jquery的首碼“$”,修改為“jQuery”,還有些朋友將預設的document等方法修改。為了讓你的外掛程式在這些東西修了了的情況下照常運行,那麼我們的做法是,把程式碼封裝裹在如下裡面:

;(function($,window,document,undefined){
    //我們的代碼。。
})(jQuery,window,document);

其實,大部分自調用匿名函數都是閉包的一種應用和寫法。

例如下面的例子

(function(document){
    var viewport;
    var obj = {
        init:function(id){
           viewport = document.querySelector("#"+id);
        },
        addChild:function(child){
            viewport.appendChild(child);
        },
        removeChild:function(child){
            viewport.removeChild(child);
        }
    }
    window.jView = obj;
})(document);
以上代碼可以改寫成如下代碼:

var f = function(document){
    var viewport;
    var obj = {
        init:function(id){
            viewport = document.querySelector("#"+id);
        },
        addChild:function(child){
            viewport.appendChild(child);
        },
        removeChild:function(child){
            viewport.removeChild(child);
        }
    }
    window.jView = obj;
};
f(document);

在這段代碼中似乎看到了閉包的影子,但 f 中沒有任何傳回值,似乎不具備閉包的條件,注意這句代碼:

window.jView = obj;
obj 是在 f 中定義的一個對象,這個對象中定義了一系列方法, 執行window.jView = obj 就是在 window 全域對象定義了一個變數 jView,並將這個變數指向 obj 對象,即全域變數 jView 引用了 obj 。而 obj 對象中的函數又引用了 f 中的變數 viewport ,因此 f 中的 viewport 不會被回收,會一直儲存到記憶體中,所以這種寫法滿足閉包的條件。 另外,我們把obj賦值給window.jView ,那麼,我們在整個window中可以直接調用obj 中的函數,但是函數內部的變數不會被收回,例如我們調用obj的init函數可以這麼寫:

window.jView.init("haorooms")
小結

以上是對閉包最簡單的理解。當然閉包還有更深入的理解。例如執行環境(execution context)、使用中的物件(activation object)以及範圍(scope)和範圍鏈(scope chain)的運行機制等等。當然,我們理解閉包先從簡單開始,寫的多了,理解的就越深了。本文也是對閉包的最簡單的入門,希望通過這篇文章,大家對閉包有了一定的理解。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.