Javascript在設計之初並沒有提供一種原生的,語言級別的模塊化方式來組織代碼,比如Java語言通過package和import來管理和使用模塊。ECMAScript 6引入了class和import的概念來支持模塊化,但是浏覽器全面支持這個標准還需要很長時間。
應用程序的模塊化指的就是通過一些高度解耦的,存放在不同地方的功能模塊構成。近年來隨著Javascript應用的復雜化,大型化,Javascript代碼需要更為有序的組織,在Javascript社區出現了很多種模塊化的實現方式,最主要的兩個規范是CommomJS和AMD,本文會重點闡述這兩個規范。
CommonJS是以在浏覽器之外構建Javascript系統而產生的項目,致力於Javascript模塊的標准化工作。主要特點是同步加載Javascript模塊,運行在服務器端。Node.js 就是CommonJS規范的一個實現。
CommonJS對於模塊的定義非常簡單,分為模塊定義(exports),模塊引用(require),模塊標示三部分。
通過全局變量 exports 返回當前模塊希望提供給其他模塊的對象:
// 定義行為
function foobar(){
this.foo = function(){
console.log('Hello foo');
}
this.bar = function(){
console.log('Hello bar');
}
}
// 把 foobar 暴露給其它模塊
exports.foobar = foobar;
通過全局函數 require 來導入其他模塊的輸出:
//使用文件與模塊文件在同一目錄
var foobar = require('./foobar').foobar,
test = new foobar();
test.bar(); // 'Hello bar'
模塊標示其實就是傳遞給require方法的參數,用來指定加載文件路徑,可以沒有後綴名.js,例如上面例子中的 “./foobar”。
CommonJS模塊的代碼都運行在模塊作用域,不會污染全局作用域,模塊可以多次加載,但是結果會被緩存。CommonJS主要是為服務器端JavaScript運行庫設計的,模塊是同步加載的,這使得難以在浏覽器中運行CommonJS代碼。Node.js上面有一些項目例如Browserify,將CommonJS帶進了浏覽器,Browserify將依賴到的單獨的js文件打包成一個單獨的js文件,統一加載到浏覽器端。AMD(異步模塊定義)是為浏覽器環境設計的,采用異步方式加載模塊,模塊的加載不影響它後面語句的運行。所有依賴這個模塊的語句,都定義在一個回調函數中,等到加載完成之後,這個回調函數才會運行。
模塊通過 define 函數定義在閉包中,格式如下:
define(id?: String /*可選*/,
dependencies?: String /*可選*/,
factory: Function|Object /*用來初始化模塊或對象的函數*/);
id 是模塊的名字,它是可選的參數。dependencies 指定了所要依賴的模塊列表,它是一個數組,也是可選的參數,每個依賴的模塊的輸出將作為參數一次傳入 factory 中。factory 是最後一個參數,它包裹了模塊的具體實現,它是一個函數或者對象。如果是函數,那麼它的返回值就是模塊的輸出接口或值。
定義一個名為 myModule 的模塊,它依賴 foo, bar 模塊:
define('myModule',
// 依賴
['foo', 'bar'],
// 依賴(foo 和 bar)被映射為函數的參數
function ( foo, bar ) {
// 返回一個定義了模塊導出接口的值
// 在這裡創建模塊
var myModule = {
doSomething:function(){
}
}
return myModule;
});
定義一個獨立模塊,不需要依賴任何其他模塊:
define(function () {
return {
doSomething: function() {}
};
});
通過 require 調用模塊:
require(['foo', 'bar'], function ( foo, bar ) {
foo.doSomething();
});
在模塊定義內部也可以使用require來加載其他模塊:
define(function ( require ) {
var isReady = false, foobar;
require(['foo', 'bar'], function (foo, bar) {
isReady = true;
foobar = foo() + bar();
});
return {
isReady: isReady,
foobar: foobar
};
});
上面的例子中 foo 和 bar沒有加載完成之前,isReady屬性為 false。
目前主要有兩個Javascript庫實現了AMD規范:require.js和curl.js。RequireJS由James Burke創建,他也是AMD規范的創始人。
CommonJS 則采用了服務器優先的策略,使用同步方式加載模塊,而且試圖涵蓋更多更寬泛的東西,例如文件IO,Promise等等。而AMD 采取了一種浏覽器優先的方式來開發,使用異步方式加載模塊。它支持對象、函數、構造器、字符串、JSON 以及其它許多類型的模塊,運行在浏覽器本地環境之中。
由於當前版本的Javascript沒有提供原生的模塊化支持,社區的開發者進行了很多模塊化的探索,使得Javascript工程化成為了可能,CommonJS和AMD就是最主要的兩個規范。