模組就是提供一個介面,卻隱藏狀態與實現的函數或物件。一般在開發中使用閉包函數來構建模組,摒棄全域性變數的濫用,規避 JavaScript 缺陷。
全域性變數是 JavaScript 最糟糕的特性之一,在一個大型 Web 應用中,全域性變數簡直就是一個魔鬼,可能帶來無窮的災難。
範例1
本範例為 String 擴充套件一個 toHTML 原型方法,該方法能夠把字串中的 HTML 跳脫字元替換為對應的字串。
//為Function增加method原型方法
Function.prototype.method = typeof Function.prototype.method === "function" ?
Function.prototype.method : //先檢測是否已經存在該方法,否則定義函數
function (name, func) {
if (!this.prototype[name]){ //檢測當前型別中是否存在指定名稱的原型
this.prototype[name] = func; //係結原型方法
}
return this; //返回型別
};
String.method('toHTML', function () { //為String增加toHTML原型方法
var entity = { //過濾的跳脫字元實體
quot : '""',
lt : '<',
gt :'>'
};
return function () { //返回方法的函數體
return this.replace(/&([^&;]+);/g, function (a, b) { //匹配字串中HTML跳脫字元
var r = entity[b]; //對映跳脫字元實體
return typeof r === 'string' ? r : a; //替換並返回
});
};
}()); //生成閉包體
在上面程式碼中,為 String 型別擴充套件了一個 toHTML 原型方法,它呼叫 String 物件的 replace 方法來查詢以
&
開頭和以
;
結束的字串。如果這些字元可以在跳脫字元實體表 entity 中找到,那麼就將該字元實體替換為對映表中的值。toHTML 方法用到了一個正規表示式。 return this.replace(/&([^&;]+);/g, function (a, b) { var r = entity[b]; return typeof r === 'string' ? r : a; }); 在最後一行使用
()
運算子立刻呼叫剛剛構造出來的函數。這個呼叫所建立並返回的函數才是 toHTML 方法。
console.log('<quot;>'); //<quot;>
console.log('<quot;>'.toHTML()); //<''>
模組利用函數作用域和閉包來建立系結物件與私有屬性的關聯。在這個範例中,只有 toHTML 方法才有權存取字元實體表 entity 這個資料物件。
模組開發的一般形式:一個定義了私有變數和函數的函數,利用閉包建立可以存取到的私有變數和函數的特權函數,最後返回這個特權函數,或者把它們儲存到可存取的地方。
使用模組可以避免全域性變數的濫用,從而保護資訊的安全性,實現優秀的設計實踐。使用這種模式也可以實現應用程式的封裝,或者構建其它框架。
模組模式通常結合範例模式使用。JavaScript 的範例就是物件字面量表示法建立的,物件的屬性值可以是數值或函數,並且屬性值在該物件的生命週期中不會發生變化。模組通常作為工具為程式其他部分提供功能支援。通過這種方式能夠構建比較安全的物件。
範例2
下面範例設計一個能夠自動生成序列號的物件。toSerial() 函數返回一個能夠產生唯一序列字串的物件。這個字串由兩部分組成:字元字首+序列號。這兩部分可以分別使用 setPrefix 和 setSerial 方法進行設定,然後呼叫範例物件的 get 方法來讀取這個字串。沒執行該方法,都會自動產生唯一一個序列字串。
var toSerial = function () { //包裝函數
var prefix = ''; //私有變數,字首字元,預設為空字元
var serial = 0; //私有變數,序列號,預設為0
return { //返回一個物件直接量
setPrefix : function (p) { //設定字首字元
prefix = String (p); //強制轉換為字串
},
setSerial : function (s) { //設定序列號
serial = typeof s == "number" ? s : 0; //如果引數不是數位,則設定為0
},
get : function () { //讀取自動生成的序列號
var result = prefix + serial;
serial += 1; //遞加序列號
return result; //返回結果
}
};
};
var serial = toSerial (); //獲取生成序列號物件
serial.setPrefix ('NO.'); //設定字首字串
serial.setSerial (100); //設定起始序號
console.log(serial.get()); //“No.100”
console.log(serial.get()); //“No.101”
console.log(serial.get()); //“No.102”
serial 物件包含的方法都沒有使用 this 或 that,因此沒有辦法損害 serial,除非呼叫對應的方法,否則不能改變 prefix 或 serial 的值。serial 物件是可變的,所以它的方法可能會被替換掉,但是替換後的方法依然不能存取私有成員。如果把 serial.get 作為一個值傳遞給第三方函數,那麼這個函數只能通過它產生唯一字串,不能通過它來改變 prefix 或 serial 的值。