利用閉包的強大威力,但從表面上看,它們似乎與回調無關。下面一起來研究其中最強大的一個:模組。
function foo() { var something = "cool"; var another = [1, 2, 3]; function doSomething() { console.log( something ); } function doAnother() { console.log( another.join( " ! " ) ); } }
正如在這段程式碼中所看到的,這裡並沒有明顯的閉包,只有兩個私人資料變數something和another,以及doSomething() 和doAnother() 兩個內部函數,它們的詞法作用域(而這就是閉包)也就是foo() 的內部作用域。
接下來考慮以下程式碼:
function CoolModule() { var something = "cool"; var another = [1, 2, 3]; function doSomething() { alert( something ); } function doAnother() { alert( another.join( " ! " ) ); } return { doSomething: doSomething, doAnother: doAnother }; } var foo = CoolModule(); foo.doSomething(); // cool foo.doAnother(); // 1 ! 2 ! 3
這個模式在JavaScript 中稱為模組。最常見的實作模組模式的方法通常被稱為模組暴露,這裡展示的是其變體。我們仔細研究一下這些代碼。
首先,CoolModule() 只是一個函數,必須透過呼叫它來建立一個模組實例。如果不執行外部函數,內部作用域和閉包都無法被建立。其次,CoolModule() 傳回一個用物件字面量語法{ key: value, ... } 來表示的物件。這個傳回的物件中含有對內部函數而不是內部資料變數的參考。我們保持內部資料變數是隱藏且私有的狀態。可以將這個物件類型的回傳值看作本質上是模組的公共API。這個物件類型的回傳值最終被賦值給外部的變數foo,然後就可以透過它來存取API 中的屬性方法,例如foo.doSomething()。
從模組中傳回一個實際的物件並不是必須的,也可以直接傳回一個內部函數。 jQuery 就是一個很好的例子。 jQuery 和$ 標識符就是jQuery 模組的公共API,但它們本身都是函數(由於函數也是對象,它們本身也可以擁有屬性)。
doSomething() 和doAnother() 函數具有涵蓋模組實例內部作用域的閉包( 透過呼叫CoolModule() 實現)。當透過傳回一個含有屬性參考的物件的方式來將函數傳遞到詞法作用域外部時,我們已經創造了可以觀察和實踐閉包的條件。如果想要更簡單的描述,模組模式需要具備兩個必要條件。
1. 必須有外部的封閉函數,該函數必須至少被呼叫一次(每次呼叫都會建立一個新的模組實例)。
2. 封閉函數必須傳回至少一個內部函數,這樣內部函數才能在私有作用域中形成閉包,並且可以存取或修改私有的狀態。
一個具有函數屬性的物件本身並不是真正的模組。從方便觀察的角度來看,一個從函數呼叫所傳回的,只有資料屬性而沒有閉包函數的物件並不是真正的模組。上一個範例程式碼中有一個叫作CoolModule() 的獨立的模組創建器,可以被呼叫任意多次,每次呼叫都會建立一個新的模組實例。當只需要一個實例時,可以對這個模式進行簡單的改進來實現單例模式:
var foo = (function CoolModule() { var something = "cool"; var another = [1, 2, 3]; function doSomething() { alert( something ); } function doAnother() { alert( another.join( " ! " ) ); } return { doSomething: doSomething, doAnother: doAnother }; })(); foo.doSomething(); // cool foo.doAnother(); // 1 ! 2 ! 3
立即調用這個函數並將返回值直接賦值給單例的模組實例標識符foo。
模組也是普通的函數,因此可以接受參數:
function CoolModule(id) { function identify() { console.log( id ); } return { identify: identify }; } var foo1 = CoolModule( "foo 1" ); var foo2 = CoolModule( "foo 2" ); foo1.identify(); // "foo 1" foo2.identify(); // "foo 2"
模組模式另一個簡單但強大的變化用法是,命名將要作為公共API 返回的對象:
var foo = (function CoolModule(id) { function change() { // 修改公共API publicAPI.identify = identify2; } function identify1() { alert( id ); } function identify2() { alert( id.toUpperCase() ); } var publicAPI = { change: change, identify: identify1 }; return publicAPI; })( "foo module" ); foo.identify(); // foo module foo.change(); foo.identify(); // FOO MODULE
reee
模組實例的內部保留對公共API 物件的內部引用,可以從內部對模組實例進行修改,包括新增或刪除方法和屬性,以及修改它們的值。 以上就是本文的全部內容,希望本文的內容對大家的學習或是工作能帶來一定的幫助,同時也希望多多支持PHP中文網! 更多JavaScript利用閉包實現模組化相關文章請關注PHP中文網!