JS模組化程式設計簡介

2020-07-16 10:05:13
模組就是提供一個介面,卻隱藏狀態與實現的函數或物件。一般在開發中使用閉包函數來構建模組,摒棄全域性變數的濫用,規避 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('&lt;quot;&gt;');  //&lt;quot;&gt;
console.log('&lt;quot;&gt;'.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 的值。