在 JavaScript 中,模塊只不過是基於函數某些特性的代碼組織方式。瀏覽器
在《你不知道的 JavaScript》中,給出了模塊模式因具有的兩個必要條件:緩存
從中咱們能夠看到一個比較重要的一點,從函數調用所返回的只有數據屬性而沒有閉包函數的對象並非真正的模塊。服務器
你看👀,理解閉包的重要性再次體現出來了。閉包
從以上要求的兩點來看,只要知足相應的條件,咱們很容易寫出一個模塊。異步
const userModule = ((name = 'module') => { let id = 1,moduleName = name; const sayName = () => { console.log('moduleName: %s', moduleName); }; const sayId = () => { console.log('id: %s', id); }; const changeName = value => { moduleName = value; }; const changePublicAPI = () => { publicAPI.sayIdentification = sayId }; const publicAPI = { sayIdentification: sayName, changeName, changePublicAPI, } return publicAPI; })();
以上在知足兩個必要的基礎上轉換成了 IIFE(當即執行函數表達式)。同時能夠看出,基於函數的模塊能夠在運行時經過內部保留着公共 API 對象的引用,從而對模塊實例進行修改。模塊化
模塊的出現也是爲了可以提升代碼的複用率,方便代碼管理。複用模塊,天然會出現模塊依賴的問題,因此說咱們須要一個管理模塊依賴的模塊。函數
const moduleManage = (() => { let modules = {}; const define = (name, deps, module) => { deps = deps.map(item => modules[item]) modules[name] = module(...deps); }; const exports = (name) => { return modules[name]; } return { define, exports, } })(); moduleManage.define('a', [], () => { const sayName = name => { console.log('name: %s', name); }; return { sayName, } }); moduleManage.define('b', ['a'], (a) => { let name = 'b'; const sayName = () => { a.sayName(name) }; return { sayName, } }); var b = moduleManage.exports('b'); b.sayName();
模塊依賴管理器也依然是個模塊,這裏的實現其實很簡單。modules[name] = module(...deps)
,使用 modules 緩存各個模塊,對於依賴模塊的模塊,則把依賴做爲參數使用。ui
CommonJS 規範服務於服務端,同步阻塞,在寫法風格上是依賴就近。可是在瀏覽器上,CommonJS 就很差使了,瀏覽器須要從服務器請求數據,下載完成後纔會有下一步的執行。若是採用 CommonJS 的同步方式,指不定何時文件纔會下載完成。code
爲了推廣到瀏覽器上,AMD 規範採用異步方式加載模塊。先異步加載模塊,加載完成後就能夠在回調中使用依賴模塊了。這樣就保證了在使用依賴時,依賴已經加載完成。AMD 規範是早早地下載,早早地執行,在回調裏 require
的是依賴的引用。在寫法風格上是依賴前置,這種風格已經不一樣於 CommonJS 了。還有,這裏早早地執行會帶來一個問題,若是存在某個依賴某些條件不成立,致使沒有用上。那麼,這裏的早早地執行豈不是畫蛇添足了?對象
CMD 規範是 sea.js 推崇的規範,它採用的也是異步加載模塊的方式,只是在依賴模塊的執行時機上有所不一樣。在寫法風格上,又迴歸到 CommonJS,依賴就近。sea.js 是早早地下載,延遲執行。
到了 ES6,終於從語法上支持模塊化了,ES6 模塊是編譯時加載,使得在編譯時就能肯定模塊的依賴關係,並且在未來服務器和瀏覽器都會支持 ES6 的模塊化方案。