放大模式or 寬放大模式(Loose augmentation)
如果一個(gè)模塊很大躁愿,必須分成幾個(gè)部分,或者一個(gè)模塊需要繼承另一個(gè)模塊杀捻,這時(shí)就有必要采用"放大模式"(augmentation)。
var module1 = (function (mod){
mod.m3 = function () {
//...
};
return mod;
})(module1);
在瀏覽器環(huán)境中麻削,模塊的各個(gè)部分通常都是從網(wǎng)上獲取的,有時(shí)無(wú)法知道哪個(gè)部分會(huì)先加載舔示。如果采用上一節(jié)的寫法碟婆,第一個(gè)執(zhí)行的部分有可能加載一個(gè)不存在空對(duì)象,這時(shí)就要采用"寬放大模式"惕稻。
var module1 = ( function (mod){
//...
return mod;
})(window.module1 || {});
構(gòu)造函數(shù)
使用new操作符完成的構(gòu)造函數(shù)竖共,比如:
var Person = function(name) {
var name = name || 'leo'; // 私有變量,通過閉包訪問
return {
// 暴露公開的成員
setName: function (name) {
name = name;
},
getName: function () {
return name;
}
};
}
var leo = new Person(); // Object{setName: function, getName: function()}
console.log(leo.getName());
每次用的時(shí)候都要new一下俺祠,也就是說每個(gè)實(shí)例在內(nèi)存里都是一份copy公给。
引用全局變量
JavaScript有一個(gè)特性叫做隱式全局變量,不管一個(gè)變量有沒有用過蜘渣,JavaScript解釋器反向遍歷作用域鏈來(lái)查找整個(gè)變量的var聲明淌铐,如果沒有找到var,解釋器則假定該變量是全局變量蔫缸,如果該變量用于了賦值操作的話腿准,之前如果不存在的話,解釋器則會(huì)自動(dòng)創(chuàng)建它拾碌,這就是說在匿名閉包里使用或創(chuàng)建全局變量非常容易吐葱,不過比較困難的是,代碼比較難管理校翔,尤其是閱讀代碼的人看著很多區(qū)分哪些變量是全局的弟跑,哪些是局部的。
匿名函數(shù)里我們可以提供一個(gè)比較簡(jiǎn)單的替代方案防症,我們可以將全局變量當(dāng)成一個(gè)參數(shù)傳入到匿名函數(shù)然后使用孟辑,相比隱式全局變量,它又清晰又快蔫敲,我們來(lái)看一個(gè)例子:
function ($, YAHOO) {
// 這里饲嗽,我們的代碼就可以使用全局的jQuery對(duì)象了,YAHOO也是一樣
} (jQuery, YAHOO));
基本的Module模式
有時(shí)候可能不僅僅要使用全局變量奈嘿,而是也想聲明全局變量喝噪,如何做呢?我們可以通過匿名函數(shù)的返回值來(lái)返回這個(gè)全局變量指么,這也就是一個(gè)基本的Module模式酝惧,來(lái)看一個(gè)完整的代碼:
var blogModule = (function () {
var my = {}, privateName = "博客園";
function privateAddTopic(data) {
// 這里是內(nèi)部處理代碼
}
my.Name = privateName;
my.AddTopic = function (data) {
privateAddTopic(data);
};
return my;
} ());
上面的代碼聲明了一個(gè)全局變量blogModule榴鼎,并且?guī)в?個(gè)可訪問的屬性:blogModule.AddTopic和blogModule.Name,除此之外晚唇,其它代碼都在匿名函數(shù)的閉包里保持著私有狀態(tài)巫财。同時(shí)根據(jù)上面?zhèn)魅肴肿兞康睦樱覀円部梢院芊奖愕貍魅肫渌娜肿兞俊?/p>
Module擴(kuò)展方式
Module模式的一個(gè)限制就是所有的代碼都要寫在一個(gè)文件哩陕,但是在一些大型項(xiàng)目里平项,將一個(gè)功能分離成多個(gè)文件是非常重要的,因?yàn)榭梢远嗳撕献饕子陂_發(fā)悍及。再回頭看看上面的全局參數(shù)導(dǎo)入例子闽瓢,我們能否把blogModule自身傳進(jìn)去呢?答案是肯定的心赶,我們先將blogModule傳進(jìn)去扣讼,添加一個(gè)函數(shù)屬性,然后再返回就達(dá)到了我們所說的目的缨叫,上代碼:
var blogModule = (function (my) {
my.AddPhoto = function () {
//添加內(nèi)部代碼
};
return my;
} (blogModule));
但是有一個(gè)問題椭符,這里擴(kuò)展的方法,好像無(wú)法調(diào)用函數(shù)內(nèi)部的私有變量耻姥,但是可以將一些需要的私有變量掛載到對(duì)象下销钝。
松耦合擴(kuò)展
上面的代碼盡管可以執(zhí)行,但是必須先聲明blogModule琐簇,然后再執(zhí)行上面的擴(kuò)展代碼蒸健,也就是說步驟不能亂,怎么解決這個(gè)問題呢婉商?我們來(lái)回想一下纵装,我們平時(shí)聲明變量的都是都是這樣的:var cnblogs = cnblogs || {} ;,確保cnblogs對(duì)象据某,在存在的時(shí)候直接用,不存在的時(shí)候直接賦值為{}诗箍,我們來(lái)看看如何利用這個(gè)特性來(lái)實(shí)現(xiàn)Module模式的任意加載順序:
var blogModule = (function (my) {
// 添加一些功能
return my;
} (blogModule || {}));
這樣每個(gè)單獨(dú)分離的文件都保證這個(gè)結(jié)構(gòu)癣籽,那么我們就可以實(shí)現(xiàn)任意順序的加載,所以滤祖,這個(gè)時(shí)候的var就是必須要聲明的筷狼,因?yàn)椴宦暶鳎渌募x取不到的匠童。缺點(diǎn): 沒辦法重寫你的一些屬性或者函數(shù)埂材,也不能在初始化的時(shí)候就是用Module的屬性。
緊耦合擴(kuò)展
緊耦合擴(kuò)展限制了加載順序汤求,但是提供了我們重載的機(jī)會(huì)俏险,看如下例子:
var blogModule = (function (my) {
var oldAddPhotoMethod = my.AddPhoto;
my.AddPhoto = function () {
// 重載方法严拒,依然可通過oldAddPhotoMethod調(diào)用舊的方法
};
return my;
} (blogModule));
通過這種方式,我們達(dá)到了重載的目的竖独,當(dāng)然如果你想在繼續(xù)在內(nèi)部使用原有的屬性裤唠,你可以調(diào)用oldAddPhotoMethod來(lái)用。
跨文件共享私有對(duì)象
如果一個(gè)module分割到多個(gè)文件的話莹痢,每個(gè)文件需要保證一樣的結(jié)構(gòu)种蘸,也就是說每個(gè)文件匿名函數(shù)里的私有對(duì)象都不能交叉訪問,那如果我們非要使用竞膳,那怎么辦呢航瞭?我們先看一段代碼:
var blogModule = (function (my) {
var _private = my._private = my._private || {},
_seal = my._seal = my._seal || function () {
delete my._private;
delete my._seal;
delete my._unseal;
},
_unseal = my._unseal = my._unseal || function () {
my._private = _private;
my._seal = _seal;
my._unseal = _unseal;
};
return my;
} (blogModule || {}));
任何文件都可以對(duì)他們的局部變量_private設(shè)屬性,并且設(shè)置對(duì)其他的文件也立即生效坦辟。一旦這個(gè)模塊加載結(jié)束刊侯,應(yīng)用會(huì)調(diào)用blogModule._seal()"上鎖",這會(huì)阻止外部接入內(nèi)部的_private长窄。如果這個(gè)模塊需要再次增生滔吠,應(yīng)用的生命周期內(nèi),任何文件都可以調(diào)用_unseal()”開鎖”挠日,然后再加載新文件疮绷。加載后再次調(diào)用_seal()”上鎖”。