前面介紹過JS中的namespace, 看起來好像我們已經(jīng)成功解決了全局空間沖突的問題了。 但前面提到的方法有一個缺點——我們無法限制對方法和變量的訪問,他們都是公開的。另一個不夠優(yōu)雅的地方就是引入了大量的this和prototype聲明,略顯繁瑣。今天要講的這個設計模式提供了一種易讀的方法解決了以上問題,也是JS中最常用的設計模式之一。
讓我們先來看一個看起來很奇怪的函數(shù):
(function() {
// some code
})();
這個有許多括號的函數(shù)叫做立即調(diào)用函數(shù)表達式IIFE,其含義就是聲明一個函數(shù)并立即執(zhí)行該函數(shù)。為什么我們要如此大費周章的弄一個立刻執(zhí)行的函數(shù)呢?回顧一下JS的基礎知識,我們知道JS中并沒有block scope這個概念,只有function才擁有自己的 scope。因此IIFE實際上創(chuàng)建了一個封閉的scope,用來模擬private scope。
對前面提到的namespace稍作修改:
var MODULE = (function() {
// some code
})();
可得到一個名為MODULE的私密封裝空間, 外界無法訪問該空間內(nèi)部。 這里為了提高可讀性,用大寫聲明模塊。
如果我們在MODULE內(nèi)部有一個叫做privateMethod的函數(shù):
var MODULE = (function() {
var privateMethod = function() {
};
})();
外界無法用類似MODULE.privateMethod()這樣的手段訪問該函數(shù), 我們成功創(chuàng)建了一個private method!同理,我們也可以創(chuàng)建private variable。
當我們創(chuàng)建了封閉的私密空間后,該空間對外界而言就是一個看不見內(nèi)部構(gòu)造的黑盒。但如果這個黑盒不能給我們提供服務,他就失去了存在的意義。為了給外界提供服務,我們還需要給這個黑盒子設置一組借口。就好比插座, 我們雖然看不到埋在墻里的部分是什么,但我們知道只要用適配的插頭接入插座, 就可以得到一定標準的電流來驅(qū)動電器。
Module Pattern中的接口由return實現(xiàn):
var MODULE = (function() {
return {
publicMethod: function() {
}
};
})();
return返回一個對象字面量,這個對象字面量就是外界可訪問的接口 : MODULE.publicMethod();
該接口可包含多個方法,這些方法可以訪問private variable和private function:
var MODULE = (function() {
var _privateName = '';
var privateMethod = function() {
};
return {
publicGetName: function() {
return _privateName;
},
publicOne: function() {
},
publicTwo: function() {
}
};
})();
于是一個幾乎完整的private scope就誕生了。在這里不得不佩服JS的前輩們,能在這么贏弱的語法上硬是搭建出了相對完善的語言結(jié)構(gòu)。
上面這個模式已近完整,可應對絕大部分情況了。但我們還可以跟前面講過的namespace結(jié)合起來,讓return部分更清晰明了:
var MODULE = (function() {
var publicObject = {};
var privateMethod = function() {};
publicObject.methodOne = function() {};
publicObject.methodTwo = function() {};
return publicObject;
})();
通過將借口的方法整合進返回對象中,return語句現(xiàn)在簡潔了許多。另外也可以不返回對象,而是先聲明方法,然后返回公共方法的引用:
var MODULE = (function() {
var privateMethod = function() {};
var methodOne = function() {};
var methodTwo = function() {};
return {
methodOne: methodOne,
methodTwo: methodTwo
};
})();
就我個人而言,我一般采用后兩種方法。若接口中方法較少教簡單,也可以采用第一種方法。
此外,module 也可以接收參數(shù):
var MODULE = (function (param) {
// some code
})(param);
甚至接收另一個module作為參數(shù):
var ModuleTwo = (function (Module) {
// access to `Module`
})(Module);
于是你會看到許多看起來不可思議又精妙絕倫的module pattern代碼,就留給各位自己去探索吧。