歷史上JavaScript一直沒有模塊體系,在其他高級(jí)語言中顾孽,Java有類文件祝钢,Python有import機(jī)制,Ruby有require若厚,PHP有include和require太颤。而JavaScript則是通過<script>標(biāo)簽引入代碼,顯得雜亂無章盹沈。人們不得不用命名空間等方式人為地約束代碼,以求達(dá)到安全和易用的目的。
為此乞封,社區(qū)指定了一些模塊加載方案做裙,最主要的有CommonJS,AMD和CMD三種肃晚。前者用于服務(wù)器锚贱,后兩者用于瀏覽器。同時(shí)关串,ES6也從語言規(guī)格的層面上拧廊,實(shí)現(xiàn)了模塊功能,完全可以代替CommonJS晋修,AMD和CMD吧碾,成為瀏覽器和服務(wù)器通用的模塊解決方案。
下面將就上面這四種方案進(jìn)行簡單介紹墓卦。
CommonJS
CommonJS是服務(wù)器端模塊的規(guī)范倦春,它的提出主要是為了彌補(bǔ)當(dāng)前JavaScript沒有標(biāo)準(zhǔn)的缺陷,以達(dá)到像Python落剪,Java具備大型應(yīng)用開發(fā)大型應(yīng)用的基礎(chǔ)能力睁本,而不是停留在小腳本程序的階段。NodeJS采取的就是CommonJS規(guī)范忠怖。
CommonJS對(duì)模塊的定義很簡單呢堰,主要分為模塊引用,模塊定義和模塊標(biāo)識(shí)3個(gè)方面凡泣。
模塊引用
模塊引用的實(shí)例如下
var math = require('math');
在CommonJS規(guī)范中枉疼,存在require()方法,這個(gè)方法接受模塊標(biāo)識(shí)问麸,以此引入一個(gè)模塊的API到當(dāng)前上下文中往衷。
模塊定義
在模塊中,上下文提供require()方法來引入外部模塊严卖。對(duì)應(yīng)引入的功能站超,上下文提供了exports對(duì)象用于導(dǎo)出當(dāng)前模塊的方法或者變量,并且它是唯一導(dǎo)出的出口掺冠。在模塊中嗡午,還存在一個(gè)module對(duì)象,它代表模塊自身稠肘,而exports是module的屬性福铅。在Node中,一個(gè)文件就是一個(gè)模塊项阴,將方法掛在在exports對(duì)象上作為屬性即可定義導(dǎo)出的方式:
// math.js
exports.add = function() {
var sum = 0,
i = 0,
args = arguments,
l = arguments.length;
while(i < 1) {
sum += args[i++];
}
return sum;
}
在另一個(gè)文件中滑黔,我們通過require()方法來引入模塊后,就能調(diào)用定義的屬性和方法了:
//program.js
var math = require('math');
exports.increment = function(val) {
return math.add(val, 1);
}
需要注意的是,module.exports
和exports
是相同的引用略荡,即
exports = module.exports
最終模塊返回的是module.exports
庵佣。
所以下面的寫法是錯(cuò)誤的
exports = function(val) {
return val + 1;
}
這樣做的話exports
就不再指向module.exports
。正確的寫法是
module.exports = function(val) {
return val + 1;
}
模塊標(biāo)識(shí)
模塊標(biāo)識(shí)其實(shí)就是傳遞給require()方法的參數(shù)汛兜,它必須是符合駝峰名
的字符串巴粪,或者以.
, ..
開頭的相對(duì)路徑,或者絕對(duì)路徑粥谬。它可以沒有文件名后綴.js肛根。
模塊的定義十分簡單,接口也十分簡潔漏策。它的意義在于將類聚的方法和變量等限定在私有的作用域中派哲,同時(shí)支持引入和引出功能以順暢的連接上下游依賴。每個(gè)模塊有獨(dú)立的空間哟玷,它們互不干擾狮辽,在引用時(shí)也顯得干凈利落。
AMD
AMD(Asynchronous Module Definition)是RequireJS在推廣過程中對(duì)模塊定義的規(guī)范化產(chǎn)出巢寡。
AMD異步加載所需的模塊喉脖,然后在回調(diào)函數(shù)中執(zhí)行主邏輯。這正是我們在瀏覽器端開發(fā)所習(xí)慣了的方式抑月。
AMD規(guī)范的模塊定義如下:
define(id?, dependencies?, factory)
它的模塊id和依賴是可選的树叽,factory的內(nèi)容就是實(shí)際代碼的內(nèi)容。下面的代碼簡單的定義了兩個(gè)模塊谦絮。
// a.js
define(function(){
var exports = {};
exports.sayHello = function() {
alert("Hello from module: " + module.id);
};
return exports;
});
// b.js
define(['a'], function(a) {
a.sayHello();
});
AMD與CommonJS規(guī)范的Node不同的是AMD模塊需要用define來明確定義一個(gè)模塊题诵,而Node是隱式包裝的,它們的目的都是為了進(jìn)行作用域的隔離层皱,僅在需要的時(shí)候被引入性锭;另一個(gè)不同是AMD的內(nèi)容需要通過返回的方式實(shí)現(xiàn)導(dǎo)出。
CMD
CMD與AMD規(guī)范的主要區(qū)別在于定義模塊和依賴模塊引入的部分叫胖。AMD需要在聲明模塊的時(shí)候指定所有的依賴草冈,通過形參傳遞到依賴模塊內(nèi)容中:
define(['dep1', 'dep2'], function(dep1, dep2){
return function(){};
});
與AMD模塊相比,CMD模塊更接近于Node對(duì)CommonJS規(guī)范的定義:
define(factory);
在依賴部分瓮增,CMD支持動(dòng)態(tài)引入怎棱,示例如下:
define(function(require, exports, module){
// 獲取模塊a的接口
var a = require('./a');
// 調(diào)用模塊a的方法
a.doSomething();
});
CommonJS, AMD, CMD 區(qū)別
CommonJS加載模塊是同步的,所以只有加載完成后才能執(zhí)行后面的操作绷跑,像NodeJS主要用于服務(wù)器端的編程拳恋,加載的模塊文件一般都已存在本地硬盤,所以加載起來比較快砸捏,不用考慮異步加載的方式谬运,所以CommonJS規(guī)范比較適用隙赁。但如果是瀏覽器環(huán)境,要從服務(wù)器加載模塊吩谦,這時(shí)就必須采用異步模式鸳谜。所以就有了AMD,CMD解決方案式廷。
- 對(duì)于依賴的模塊,AMD是提前執(zhí)行芭挽,CMD是推遲執(zhí)行滑废。
- CMD推崇依賴就近,AMD推崇依賴前置袜爪。
// CMD
define(function(require, exports, module)){
var a = require('./a');
a.doSomething();
var b = require('./b');
b.doSomething();
}
// AMD
define(['./a', './b'], function(a, b) {
a.doSomething();
b.doSomething();
});