公司新來的女實習生問我什麼是閉包?

觀感度:🌟🌟🌟🌟🌟javascript

口味:冰鎮西瓜html

烹飪時間:20min前端



撩妹守則第一條,女孩子都喜歡童話故事。java

那就先來說一個童話故事~程序員

// 有一個公主
// 她生活在一個充滿冒險的奇妙世界裏
// 她碰見了她的白馬王子,帶着她騎着獨角獸環遊世界
// 與龍搏鬥,遇到了會說話的松鼠,以及許多其餘幻想的事情。
function princess () {
    var adventrures = [];
    function princeCharming () {};
    var unicorn = {};
    var dragons = [];
    var squirrel = "Hello!";
    // 但她不得不回到她那充滿家務和大人們的單調世界。
    return {
    // 她常常給身邊的人講她做爲一個公主的奇妙經歷。
      story:function () {
          return adventures[adventures.length - 1];
      }
    }
}
// 但他們看到的只是一個小女孩在講述關於魔法和幻想的故事
var littleGril = princess();
littleGril.story();
// 即便大人們知道她是真正的公主,他們也不會相信所謂的獨角獸或龍,由於他們永遠看不到它們
// 大人們說它們只存在於小女孩的想象中
// 但咱們知道真正的真理
// 裏面的小女孩真的是個公主
複製代碼

這個故事來自於stackoverflow的一則回答,看不懂不要緊,等閱讀完本文後,回頭再來看這個故事,你會發現你已經徹底瞭解了個人魅力,咳咳@¥%#…………JavaScript中閉包的魅力。算法

什麼是閉包?

當函數能夠記住並訪問所在的詞法做用域時,就產生了閉包,即便函數是在當前詞法做用域以外執行。 --- 你不知道的JavaScript(上卷)瀏覽器

來個🌰ruby

function demo() {
    var a = 1;
    return function () {
        return a; 
    }
}

var a = demo();
console.log(a());  // 1
複製代碼

閉包的構成

閉包由兩部分構成:函數,以及建立該函數的環境。閉包

環境由閉包建立時在做用域中的任何局部變量組成。函數

閉包的本質

閉包實際上是JavaScript函數做用域的反作用產品。

閉包是一種特殊的對象。

所謂有意栽花花不開,無意插柳柳成蔭,不是JavaScript故意要使用閉包,而是因爲JavaScript的函數內部可使用函數外部的變量,這段代碼又剛恰好符合閉包的定義。

JavaScript中,外部函數調用以後其變量對象本應該被銷燬,但閉包阻止了它們的銷燬,咱們仍然能夠訪問外部函數的變量對象。

進一步的說,一般狀況下,函數的做用域及其全部變量都會在函數執行結束後被銷燬。可是,若是建立了一個閉包的話,這個函數的做用域就會一直保存到閉包不存在爲止。

function addCalculator (x) {
    return function (y) {
        return x + y;
    }
}

var add1 = addCalculator(1);

console.log(add1(1)); //2

// 釋放對閉包的引用
add1 = null;

console.log(add1(1)); //Uncaught TypeError: add1 is not a function

複製代碼

閉包的應用

咱們能夠用閉包來作什麼呢?

瞭解Java的同窗可能知道,Java是支持私有方法的,私有方法只能被一個類中的其餘方法所調用,可是JavaScript沒有提供這種原生支持,因此咱們能夠經過閉包來模擬私有方法。

私有方法天然有私有方法的好處,私有方法有利於限制對代碼的訪問,並且能夠避免非核心的方法干擾代碼的公共接口,減小全局污染。

來個🌰

var calculator = (function(){
    var a = 1;
    function addCalculator(val){
        a += val
    }
    return {
        add1:function() {
            addCalculator(1);
        },
        add2:function() {
            addCalculator(2);
        },
        result:function() {
            return a
        }
    }
})();

console.log(calculator.result());  // 1
calculator.add1();
console.log(calculator.result());  // 2
calculator.add2();
console.log(calculator.result());  // 4
複製代碼

上面這種方式也叫作模塊模式(module pattern)

使用閉包的注意事項

內存泄漏

由於閉包可使函數中的變量都保存在內存中,形成很大的內存消耗,因此若是 不是某些特定的任務須要使用閉包,咱們不要濫用它。

不少博客中都提到了這一點,可是其實都是不徹底對的。

敲黑板!!!

使用不當的閉包會在IE(IE9)以前形成內存泄漏問題。由於它的JavaScript引擎使用的垃圾回收算法是引用計數法,對於循環引用將會致使GC(下文會介紹)沒法回收垃圾。

關於各個瀏覽器的閉包測試,詳情請見司徒正美-js閉包測試

垃圾回收機制

都9102年了,全國開始實行垃圾分類了,你竟然還不知道垃圾回收機制,趕快來補習一下!

垃圾回收也就是GC(Garbage Collection)

GC把程序不用的內存空間視爲垃圾,找到它們而且將它們回收,讓程序員能夠再次利用這部分空間。

不是全部的語言都有GC,通常存在於高級語言中,如JavaJavaScriptPython。那麼在沒有GC的世界裏,程序員就比較辛苦,只能手動去管理內存,好比在C語言中咱們能夠經過malloc/free,在C++中的new/delete來進行管理。

垃圾回收算法

由於這一部分的內容不少,本文只進行簡單的講解,若是想深刻了解垃圾回收算法的同窗能夠在文末獲取學習資料。

GC標記-清除算法

世界上首個值得記念的GC算法是GC標記-清除算法。由於自其問世以來,一直到半個世紀後的今天,它依然是各類處理程序所用的偉大的算法。

GC標記-清除算法由標記階段和清除階段構成,標記階段將全部的活動對象作上相應的標記,清除階段把那些沒有標記的對象,也就是非活動對象進行回收。在搜索對象並進行標記的時候使用了深度優先搜索,儘量的從深度上搜索樹形結構。

優勢:

1.算法簡單,實現容易。

2.與保守式的GC算法兼容。

缺點:

1.在使用過程當中會出現碎片化的狀況,如同Windows的文件系統同樣,致使 無數的小分塊散佈在堆的各個地方。

2.分配速度,因爲分塊的不連續性,算法每次分配的時候都須要遍歷空閒鏈表爲了找到足夠大的分塊,這樣最糟糕的狀況就是遍歷到最後才找到合適的分 塊,影響了分配速度。

引用計數法

這種方法中引入了計數器的概念,經過計數器來表示對象的「人氣指數」,也就是有多少個程序引用了這個對象。當計數器(引用數)爲0時,垃圾馬上被回收。

優勢:

1.能夠當即回收垃圾。

2.最大暫停的時間短。

3.而且沒有必要沿指針查找。

缺點:

1.上文提到過的循環引用沒法回收。

2.而且實現起來很複雜。

3.計數器值的增減處理十分繁重。

4.同時計數器須要佔不少位,致使內存空間的使用效率大大下降。

軟件工程沒有銀彈,這些缺點也都有相應的辦法進行解決,若是你想深刻了解垃圾回收算法,能夠購買垃圾回收的算法與實現這本書去看,建議支持正版。

歡迎關注個人我的公衆號,文章將同步發送,後臺回覆【福利】便可免費領取海量學習資料。

你的前端食堂,記得按時吃飯。

ps:你也能夠後臺回覆垃圾回收便可領取相關資料一份~

相關文章
相關標籤/搜索