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就是最主要的兩個規范。