深刻理解javascript系列(九):應用閉包

理論是自信的基礎,結合理論的實踐才能讓咱們走的更遠。javascript

前兩個系列,我記錄了閉包的學習,如何利用閉包解決實際問題了?其實,不少東西你我都知道,不是一蹴而就的,不是你今天學了就會了,還須要屢次練習,反覆練習。相信終究一天你我會運用自如。java

下面就經過3個小例子,來運用閉包解決實際問題吧。面試

9.1  循環、setTimeout與閉包

在面試題中經常會遇到一個與循環、閉包有關的問題,以下:設計模式

//利用閉包的知識修改這段代碼,讓代碼的執行結果爲隔秒輸出1,2,3,4,5
for (var i = 1; i<=5; i++) {
    setTimeout(timer=()=>{
        console.log(i)
    },i*1000)
}複製代碼

首先來分析一下若是直接運行這個例子會輸出什麼結果。(涉及到定時器線程的相關知識還須要自行學習)bash

前面咱們已經知道,for循環的大括號並不會造成本身的做用域,所以這個時候確定是沒有閉包產生的,而i值做爲全局的一個變量,會隨着循環的過程遞增。所以循環結束以後,i變成了6。微信

而每個循環中,setTimerout的第二個參數訪問的都是當前的i值,所以第二個i值分別是1,2,3,4,5。而第一個參數timer函數中雖然訪問的是同一個i值,可是因爲延遲的緣由,當timer函數被setTimerout運行時,循環已經結束,即i已經變爲6了。閉包

所以這段代碼執行結果是隔秒輸出6.框架

而咱們想要的是隔秒輸出1,2,3,4,5,所以須要藉助閉包的特性,將每個i值都用一個閉包保護起來。每一輪循環,都把當前的i值保存在一個閉包中,當setTimerout中定義的操做執行時,訪問對應閉包便可。模塊化

這個時候咱們回想一下閉包造成的條件,簡單來講,就是一個函數中定義了一個子函數,子函數內部訪問了函數的變量對象。所以咱們只須要建立一個這樣的環境便可。函數

for (var i = 1; i<=5; i++) {
    (function(i) {
        setTimeout(timer=()=>{        console.log(i)
    },i*1000)    })(i)
}複製代碼

定義一個匿名函數,稱做A,並將其看成閉包環境。而timer函數則做爲A的內部函數,當A執行時,只需訪問A的變量對象便可。所以將i值做爲參數傳入,這樣也就知足了閉包的條件,並將i值保存在了A中。

一樣的道理也能夠在timer函數裏作文章。還有更多方法,這裏就不一一贅述。

9.2  單例模式與閉包

好,我繼續個人筆記。

在javascript中有許多解決特定問題的編碼思惟(設計模式,Alloy Team出的那本我的以爲很棒),例如工廠模式、發佈訂閱模式、裝飾者模式、單例模式等。其中,單例模式是早期開發最經常使用的模式之一,而它的實現,與閉包慼慼相關。

所謂單例模式,就是隻有一個實例。

1.  最簡單的單例模式

對象字面量的方法就是最簡單的單例模式,咱們能夠將屬性與方法依次放在字面量裏。

var person = {
    name: 'pan',
    age: '18',
    getName: function() {
        return this.name
    },
    getAge: function() {
        return this.age
    }
}複製代碼

可是這樣的單例模式有一個嚴重的問題,即它的屬性能夠被外不修改。所以在許多場景中,這樣的寫法並不符合咱們的需求,咱們更指望對象可以有本身的私有方法與屬性。

PS:我的以爲不論學習什麼,都應該回顧它的歷史,以便更利於當下的學習。理論性知識發展到目前,不是偶然,總有那麼一個階段性的過渡。司徒正美在他的做品《javascript框架設計》前言中就說到了這麼一個事「當初,我閱讀jQuery源碼,最初看的是1.4.3版本,看得一頭霧水,一氣之下,從最初的1.0版本開始看。看完全部版本,瞭解其迭代過程,才明白」。

2.  有私有方法/屬性的單例模式

經過前面所學的知識咱們很容易就能想到,想要一個對象擁有本身私有的方法屬性,那麼只須要建立一個單獨的做用域將對象與外界隔離起來就好了。這裏咱們藉助匿名函數自執行的方式便可。

var person = (function() {
     var name = 'pan';
     var age = 18;
 
     return{
        function getName() {        return name
     };
     function getAge() {
         return age
     }    }   
})();
person.getName();複製代碼

私有變量的好處在於,外界對於私有變量可以進行什麼樣的操做是能夠控制的。咱們能夠提供一個getName方法讓外界能夠訪問名字,也能夠額外提供一個setName方法,來修改它的名字。對外提供什麼樣的能力,徹底由咱們本身決定。

如今咱們正走在模塊化的道路上....

3.  調用時才初始化的單例模式

有的時候(使用頻次較少)咱們但願本身的實例僅僅只是在調用的時候才被初始化,而不像上面兩個例子那樣,即便沒有調用person,person的實例在函數自執行的時候就返回了。

那麼咱們就須要在上面例子的基礎上作一點小小的改動了。

var person = (function(){
    //定義一個變量,用來保存實例
    var instance = null;
    var name = 'pan';
    var age = 18;

    //初始化方法
    function init() {
        return{
            getName: function() { return name;},
            getAge: function() { return age;}
        }
    }

    return {
        getInstance: function() {
                if(!instance){
                    instance = init()
                }
                return instance
            }
    }
})();

//只在使用時獲取實例
var p1 = person.getInstance();複製代碼

在這個例子中,咱們對匿名函數中定義了一個instance變量用來保存實例。在getInstance方法中判斷了是否對他進行從新賦值。因爲這個判斷的存在,所以變量instance僅僅只在第一次調用getInstance方法時賦值了。

在寫一個系列,我將持續更新我閉包相關應用。感謝陽波大神。

這些都是我以往的學習筆記。若是您看到此筆記,但願您能指出個人錯誤。有這麼一個羣,裏面的小夥伴互相監督,堅持天天輸出本身的學習心得,不輸出就出局。但願您能加入,咱們一塊兒終身學習。歡迎添加個人我的微信號:Pan1005919589

相關文章
相關標籤/搜索