利用閉包的強大威力,但從表面上看,它們彷佛與回調無關。下面一塊兒來研究其中最強大的一個:模塊。閉包
1 function foo() { 2 var something = "cool"; 3 var another = [1, 2, 3]; 4 function doSomething() { 5 console.log( something ); 6 } 7 function doAnother() { 8 console.log( another.join( " ! " ) ); 9 } 10 }
正如在這段代碼中所看到的,這裏並無明顯的閉包,只有兩個私有數據變量something和another,以及doSomething() 和doAnother() 兩個內部函數,它們的詞法做用域(而這就是閉包)也就是foo() 的內部做用域。
接下來考慮如下代碼:ide
1 function CoolModule() { 2 var something = "cool"; 3 var another = [1, 2, 3]; 4 5 function doSomething() { 6 alert( something ); 7 } 8 function doAnother() { 9 alert( another.join( " ! " ) ); 10 } 11 return { 12 doSomething: doSomething, 13 doAnother: doAnother 14 }; 15 } 16 var foo = CoolModule(); 17 foo.doSomething(); // cool 18 foo.doAnother(); // 1 ! 2 ! 3
這個模式在JavaScript 中被稱爲模塊。最多見的實現模塊模式的方法一般被稱爲模塊暴露,這裏展現的是其變體。咱們仔細研究一下這些代碼。函數
首先,CoolModule() 只是一個函數,必需要經過調用它來建立一個模塊實例。若是不執行外部函數,內部做用域和閉包都沒法被建立。其次,CoolModule() 返回一個用對象字面量語法{ key: value, ... } 來表示的對象。這個返回的對象中含有對內部函數而不是內部數據變量的引用。咱們保持內部數據變量是隱藏且私有的狀態。能夠將這個對象類型的返回值看做本質上是模塊的公共API。這個對象類型的返回值最終被賦值給外部的變量foo,而後就能夠經過它來訪問API 中的屬性方法,好比foo.doSomething()。spa
從模塊中返回一個實際的對象並非必須的,也能夠直接返回一個內部函
數。jQuery 就是一個很好的例子。jQuery 和$ 標識符就是jQuery 模塊的公
共API,但它們自己都是函數(因爲函數也是對象,它們自己也能夠擁有屬
性)。code
doSomething() 和doAnother() 函數具備涵蓋模塊實例內部做用域的閉包( 經過調用CoolModule() 實現)。當經過返回一個含有屬性引用的對象的方式來將函數傳遞到詞法做用域外部時,咱們已經創造了能夠觀察和實踐閉包的條件。若是要更簡單的描述,模塊模式須要具有兩個必要條件。對象
1. 必須有外部的封閉函數,該函數必須至少被調用一次(每次調用都會建立一個新的模塊實例)。
2. 封閉函數必須返回至少一個內部函數,這樣內部函數才能在私有做用域中造成閉包,而且能夠訪問或者修改私有的狀態。
一個具備函數屬性的對象自己並非真正的模塊。從方便觀察的角度看,一個從函數調用所返回的,只有數據屬性而沒有閉包函數的對象並非真正的模塊。上一個示例代碼中有一個叫做CoolModule() 的獨立的模塊建立器,能夠被調用任意屢次,每次調用都會建立一個新的模塊實例。當只須要一個實例時,能夠對這個模式進行簡單的改進來實現單例模式:blog
1 var foo = (function CoolModule() { 2 var something = "cool"; 3 var another = [1, 2, 3]; 4 5 function doSomething() { 6 alert( something ); 7 } 8 function doAnother() { 9 alert( another.join( " ! " ) ); 10 } 11 return { 12 doSomething: doSomething, 13 doAnother: doAnother 14 }; 15 })(); 16 foo.doSomething(); // cool 17 foo.doAnother(); // 1 ! 2 ! 3
當即調用這個函數並將返回值直接賦值給單例的模塊實例標識符foo。
模塊也是普通的函數,所以能夠接受參數:ip
1 function CoolModule(id) { 2 function identify() { 3 console.log( id ); 4 } 5 return { 6 identify: identify 7 }; 8 } 9 var foo1 = CoolModule( "foo 1" ); 10 var foo2 = CoolModule( "foo 2" ); 11 foo1.identify(); // "foo 1" 12 foo2.identify(); // "foo 2"
模塊模式另外一個簡單但強大的變化用法是,命名將要做爲公共API 返回的對象:作用域
1 var foo = (function CoolModule(id) { 2 function change() { 3 // 修改公共API 4 publicAPI.identify = identify2; 5 } 6 function identify1() { 7 alert( id ); 8 } 9 function identify2() { 10 alert( id.toUpperCase() ); 11 } 12 var publicAPI = { 13 change: change, 14 identify: identify1 15 }; 16 return publicAPI; 17 })( "foo module" ); 18 foo.identify(); // foo module 19 foo.change(); 20 foo.identify(); // FOO MODULE
經過在模塊實例的內部保留對公共API 對象的內部引用,能夠從內部對模塊實例進行修改,包括添加或刪除方法和屬性,以及修改它們的值。io