JS模塊化

完全不封裝時

最開始的js是很簡單的,它並不承擔很複雜的功能,於是所有的變量和函數都寫在全局作用域中(Global)。這樣一開始沒有什麼問題,但是當JS的代碼越來越多,後來慢慢出現插件或多個JS文件時就有問題了,變量和函數的名稱很容易衝突。

function foo(){
    //...
}
function bar(){
    //...
}

利用JS對象模擬命名空間

利用JS對象,將相關的方法和屬性封裝一層,這樣可以避免很多同功能函數的衝突。

var MYAPP = {
    foo: function(){},
    bar: function(){}
}

MYAPP.foo();

但是這樣只能做到簡單的減少全局中變量的衝突,關於安全方面完全沒有幫助,還是可以隨便覆蓋和訪問。

利用函數的塊級作用域

隱藏局部變量

塊級作用域裏的變量外面是訪問不到的,利用這一點可以將我們的功能封裝在一個匿名函數中,只通過返回值暴露出我們想暴露出的接口。

var Module = (function(){
    var _private = "safe now";
    var foo = function(){
        console.log(_private)
    }

    return {
        foo: foo
    }
})()

Module.foo();
Module._private; // undefined

利用函數傳參添加依賴

就是醬咯,這種封裝的方式已經把現代模塊化代碼上的要素基本都包含了。
獨立性是模塊的重要特點,模塊內部最好不與程序的其他部分直接交互。
爲了在模塊內部調用全局變量,必須顯式地將其他變量輸入模塊。

var Module = (function($){
    var _$body = $("body");     // we can use jQuery now!
    var foo = function(){
        console.log(_$body);    // 特權方法
    }

    // Revelation Pattern
    return {
        foo: foo
    }
})(jQuery)

Module.foo();

繼承或拓展模塊

利用傳參的特性,可以繼承已有的模塊:

    var module1 = (function (mod){
    mod.m3 = function () {
      //...
    };
    return mod;
  })(module1);

模塊管理

當我們有多個js的時候我們會寫很多的script標籤,但是這樣不僅增加了HTTP請求,而且難以維護,因爲執行順序是從上至下的,所以被依賴的需要放在前面,文件多了就比較尷尬了。

CommonJS

定義模塊

上下文提供了exports對象用於導出當前模塊的方法或者變量,並且它是唯一的導出的出口。在模塊中,還在 一個module對象,它代表模塊自身,exports是module的屬性。將方法掛載在exports上作爲屬性即可。

exports.add = function () {   
    var sum = 0,   
    i = 0,     
    args = arguments,     
    l = args.length;   
    while (i < l) { 
        sum += args[i++]; 
      }   
    return sum; 
};

使用模塊

使用時就在要使用的文件中引用:

var selfModule = require('selfModule');
console.log(selfModule.add(13,3));

在引用的過程中,根據具體實現的不同,使用的模塊標識也不同,絕對路徑是一般都支持的。

同步加載

在你require的時候,是同步加載這個模塊的,也就是說,這個模塊中的代碼執行完纔會繼續執行主文件中下面的代碼。
可以試下下面這個例子:

var EXE_TIME = 2;

(function(second){
    var start = +new Date();
    while(start + second*1000 > new Date()){}
})(EXE_TIME)

console.log("2000ms executed");
var timeout = require('E:\\Git\\NodeTest\\timeout.js');
console.log('done!');

這種阻塞式的加載在服務器端是沒有問題的,比如node。但是在瀏覽器端,阻塞式加載並不是很理想。

AMD

定義模塊

在這裏,一個磁盤文件應該只定義一個模塊
如果一個模塊僅含值對,沒有任何依賴,則在define()中定義這些值對就好了:

define({
    color: "black",
    size: "unisize"
});

如果一個模塊沒有任何依賴,但需要一個做setup工作的函數,則在define()中定義該函數,並將其傳給define():

define(function () {
    //Do setup work here

    return {
        color: "black",
        size: "unisize"
    }
});

如果模塊存在依賴:則第一個參數是依賴的名稱數組;第二個參數是函數,在模塊的所有依賴加載完畢後,該函數會被調用來定義該模塊,因此該模塊應該返回一個定義了本模塊的object。依賴關係會以參數的形式注入到該函數上,參數列表與依賴名稱列表一一對應。

define(["./cart", "./inventory"], function(cart, inventory) {
        //return an object to define the "my/shirt" module.
        return {
            color: "blue",
            size: "large",
            addToCart: function() {
                inventory.decrement(this);
                cart.add(this);
            }
        }
    }
);

使用模塊

require(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){
    // some code here
});

模塊的加載

加載模塊時默認要加載的模塊和加載它們的文件在同一個目錄下,但是如果不是你可以通過require.config來配置。

CMD

CMD的定義模塊部分與AMD有區別,更加接近於CommonJS。

define(function(require, exports, module){
    var a = require("a");
    a.doSomething();
    var b = require("b");
    b.doSomething();    // 依賴就近,延遲執行
})

兼容模塊規範

爲了能將一個模塊可以同時運行在使用不同包管理的地方,我們可以這樣定義模塊:

(function (name, definition) {   
    // 檢測上文環境是否爲AMDCMD   
    var hasDefine = typeof define === 'function',     
    // 檢查上文環境是否爲Node     
    hasExports = typeof module !== 'undefined' && module.exports;  
    if (hasDefine) {    
        // AMD環境CMD環境     
        define(definition);   
    } else if (hasExports) {     
        // 定義爲通用Node模塊     
        module.exports = definition();   
    } else {     
        // 將模塊的執行結果掛在window變量中,在瀏覽器中this指向window對象     
        this[name] = definition();   
    } 
})('math', function () {   
    var interface = {};
    interface.add=function () {   
        var sum = 0,   
        i = 0,     
        args = arguments,     
        l = args.length;   
        while (i < l) { 
            sum += args[i++]; 
          }   
        return sum; 
    };
    return interface; 
});
發佈了128 篇原創文章 · 獲贊 6 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章