JavaScript模塊的前世今生
如今JavaScript模塊化編程的概念已經普及開來,一提起模塊化,大家想到的可能是AMD,CMD,requirejs或seajs。其實還有很多其他的概念。本文將會陳述下JavaScript模塊的前世今生。
眾所周知,JavaScript由于歷史的原因并沒有模塊的概念,自從ajax帶來了web2.0概念后,js代碼已經和以前大不相同了,2009年HTML5興起后,前端代碼的行數已經呈現井噴式發展,隨著代碼量的增加,模塊的缺失的缺點日益凸顯,Javascript社區做了很多探索。
模塊的定義
模塊并非js語言獨創,顯然是借鑒其他語言的,下面是百度百科對模塊的定義:
模塊,又稱構件,是能夠單獨命名并獨立地完成一定功能的程序語句的集合(即程序代碼和數據結構的集合體)
</blockquote>從中提煉出幾個關鍵字就是,獨立,集合,完成一定功能。
上面的提煉,再從其他語言的實現中借鑒下,總結起來,我們期待的模塊有如下特性:
- 獨立性——能夠獨立完成一個功能,不受外部環境的影像
- 完整性——完成一個特定功能
- 集合性——一組語句的集合
- 依賴性——可以依賴已經存在的模塊
- 被依賴——可以被其他模塊依賴
</ul>其實我們想要的就是一個獨立的模塊,并能引用依賴,及被依賴。
C語言的庫和頭文件(include),java的包(import)。這在其他語言中都是原生支持的特性,在js中卻是沒有的。
原始寫法
如果僅從定義入手,那么一個函數即可成為一個模塊(獨立,集合,完成一個功能),那我們就先從最原始的探索開始,也許不經意間,我們早已在使用模塊了。
//最簡單的函數,可以稱作一個模塊 function add(x, y) { return x + y; }稍微了解點javascript基礎的人都知道js中能創建作用域的就是函數(ES6之前),總結下社區的探索,對模塊的模擬大概如下:
(function (mod, $, _) { mod.add = ***; mod.sub = ***; }((window.mod = window.mod || {}), jQuery, Underscore));上面的mod模塊不會重復定義,可自由定義依賴。
99%的人思想會止步于此,但這種實現其實并不完美,仍然需要手動維護依賴的順序。典型的場景就是上面的jquery必須先于我們的代碼引入,不然會報引用錯誤,這顯然不是我們想要的。
我在寫Painter的時候,曾經手動維護幾十個script之間的先后順序,這種感覺很虐心,最后想加個新script很容易報錯。下面介紹的
YUI
前段時間雅虎宣布YUI不再更新了,很是傷感,最早接觸模塊的概念,當屬YUI了,當然不是YUI2了。
YUI3經過全新設計,使用了沙箱模式 + 命名空間的方式,并有了模塊的概念。
例如在YUI3中想使用一個模塊,需要如下這樣:
//使用node模塊,node模塊會作為參數傳入 YUI().use('node', function (Y) { ///*** }YUI的模塊化已經做的很好了,但對于僅想使用模塊的人,要引入YUI確實有點太重了。
CMD(Common Module Definition)
說道CMD就不能不提commonjs,提到commonjs就不能不提node。
CMD規范參照commonjs中的方式,定義模塊的方式如下:
define(function(require, exports, module) {// The module code goes here });</pre>
一個文件就是一個模塊,文件名就是模塊的名字,使用模塊的方法也和commonjs中一致,只需require就好了,模塊名字可省略后綴。
//使用event.js模塊 var ec = require('event');CMD的典型實現就是seajs,應用的很廣泛。
AMD(Asynchronous Module Definition)
[AMD](https://github.com/amdjs/amdjs-api/wiki/AMD-(%E4%B8%AD%E6%96%87%E7%89%88)是異步模塊定義,特別適合在瀏覽器端使用,其規范和CMD是很像的,AMD規范中定義模塊的方式如下:
define(id?, dependencies?, factory);同CMD一樣,一個文件即一個模塊,模塊的使用方法如下:
define(["beta"], function (beta) { bata.***//調用模塊 });AMD主張依賴注入,這點和CMD不同(以來查找)。
AMD也支持已CMD的方式來使用依賴。
AMD的典型實現有requireJS,modJS和lodJS。
KMD
KMD是kissy中提出來的,是kissy自己的一套模塊化方案,具體我也不是很清楚,感興趣的同學可自行搜索相關資料。
有一次同事@eric曦堯無意說起,KMD的意思是 kill amd and cmd,當時覺得好高打上的名字(/ □ \)。
ES6
ES6帶來了語言層面的模塊化支持,規范方面見這里,文檔方面見這里。
我們現在期待的就是ES6規范快點塵埃落定(據說今年夏天),現在還處于草案狀態,還有瀏覽器廠商們的大力支持,還有就是在國內盡快普及開來。
UMD
UMD的全稱是Universal Module Definition。和它名字的意思一樣,這種規范基本上可以在任何一個模塊環境中工作。
一段典型的UMD代碼如下所示:
(function (root, factory) { var Data = factory(root); if ( typeof define === 'function' && define.amd) { // AMD define('data', function() { return Data; }); } else if ( typeof exports === 'object') { // Node.js module.exports = Data; } else { // Browser globals var _Data = root.Data;Data.noConflict = function () { if (root.Data === Data) { root.Data = _Data; } return Data; }; root.Data = Data; }
}(this, function (root) { var Data = ... //自己的代碼 return Data; }));</pre>
這是出自data.js中的一部分代碼,其原理就是做個判斷,不同的環境進行不同的處理。
我已將UMD應用到自己的項目中,瞬間感覺高大上了不少:-)。
總結
比較成氣候的模塊化方案,當屬AMD和CMD,網上關于二者比較的文章甚多,很難評價誰好誰壞,當下開來AMD的使用范圍似乎更廣些,而CMD的本土化方面做的更好。
這些模塊化的探索,使前端工程化成為了可能,可以說沒有模塊,工程化更無從彈起,本文總結了大家在模塊化方面的一些探索,下一篇文章將重點介紹下lodJS(一款基于AMD的模塊加載器)的實踐和原理。
參考資料