JavaScript 模塊的發(fā)展史

我們都知道 JavaScript 中并沒有模塊的概念,一開始 JavaScript 的出現(xiàn)只是作為簡單腳本語言來實現(xiàn)簡單的頁面邏輯,而隨著互聯(lián)網(wǎng)的發(fā)展和 web 2.0 時代的到來,前端代碼呈現(xiàn)井噴式發(fā)展,隨著代碼量的增加,模塊缺失的問題日益凸顯,而同時 JavaScript 社區(qū)也做了很多探索。

那么什么是模塊呢?

模塊,是指能夠單獨命名并獨立地完成一定功能的程序語句的集合(即程序代碼和數(shù)據(jù)結(jié)構(gòu)的集合體)。

所以,模塊的核心就是需要完成特定的功能,并且其很重要的一點就是需要解決引用依賴以及被依賴的問題。

函數(shù)

從定義上來說,其實函數(shù)也可以被當(dāng)作是一個模塊。

在 myModule.js 中新增如下函數(shù)

function add(x, y) {
  return x + y;
}
function minus(x, y) {
  return x - y;
}

通過 script 標(biāo)簽引入 myModule.js 后可直接使用里面的函數(shù)

<script src="myModule.js"></script>
<script>
  console.log(add(1, 2)) // 3
  console.log(minus(2, 1)) // 1
</script>

在 myModule.js 中,每一個函數(shù)都可以被認(rèn)為是一個模塊。通過函數(shù)方式定義模塊主要有兩個缺陷:一是會污染全局變量,無法保證各模塊間的變量名不沖突;二是需要手動維護(hù)依賴的順序,如果 myModule.js 中的模塊需要依賴其它模塊(如 jQuery),則該模塊(jQuery)需要在 myModule.js 之前引用。

CommonJS

自 2009 年 NodeJS 誕生后,JavaScript 模塊化編程正式進(jìn)入人們的視野,而 NodeJS 的模塊化系統(tǒng),便是參照 CommonJS 規(guī)范實現(xiàn)的。

在 CommonJS 規(guī)范中,一個文件就是一個模塊,每個模塊都有各自的作用域,即在一個模塊中的變量、函數(shù)是私有的,外部無法訪問。

在模塊內(nèi)部,module 變量對象代表當(dāng)前模塊,其 exports 屬性也是一個對象,代表對外的接口,所以我們將需要對外暴露的內(nèi)容放進(jìn) module.exprots 對象即可;而引用模塊則使用 require 函數(shù)。

使用 CommonJS 規(guī)范來定義 myModule 模塊,在 myModule.js 中寫入如下代碼

function add(x, y) {
  return x + y;
}
function minus(x, y) {
  return x - y;
}
module.exports = {
  add: add,
  minus: minus
}

在 main.js 中引用 myModule 模塊

var myModule = require('./myModule.js');
console.log(myModule.add(1,2)); // 3
console.log(myModule.minus(2,1)); // 1

但是,由于在 CommonJS 規(guī)范中,require 加載模塊的方式是同步加載,使得其不適合在瀏覽器端使用。在服務(wù)器端同步加載模塊,等待時間取決于硬盤的讀取時間,而在瀏覽器端同步加載模塊,等待時間取決于網(wǎng)速快慢,這使得在等待加載模塊的過程中,瀏覽器會處于假死的狀態(tài)。

AMD(Asynchronous Module Definition)

AMD 即異步模塊定義,是 RequireJS 在推廣過程中對模塊定義的規(guī)范化產(chǎn)出。同樣的,其規(guī)定一個文件就是一個模塊,文件名即模塊名。

AMD 使用 define 函數(shù)定義模塊;使用 require 函數(shù)引用模塊。

define 函數(shù)使用方式如下

define(id?, dependencies?, factory);

定義 myModule.js 模塊

define(['depenModule'], function (depenModule) {
  //do something
});

require 接收兩個參數(shù),第一個參數(shù)為所依賴的模塊標(biāo)識數(shù)組;第二個參數(shù)為依賴模塊加載完成后的回調(diào)函數(shù)。

在 main.js 中引用 myModule 模塊

require(['myModule'], function (myModule){
  //do something
});

AMD 推崇依賴前置,需要先異步加載其所需的依賴模塊后才會執(zhí)行相應(yīng)回調(diào)函數(shù)中的代碼。

CMD(Common Module Definition)

CMD 即公共模塊定義,是 SeaJS 在推廣過程中對模塊定義的規(guī)范化產(chǎn)出。同樣的,其規(guī)定一個文件就是一個模塊,文件名即模塊名。

其使用 define 函數(shù)定義模塊;使用 require 函數(shù)引用模塊。

define 函數(shù)使用方式如下

define(factory)

定義 myModule.js 模塊

define(function(require, exports, module) {
  //do something
});

在 main.js 中引用 myModule 模塊

var myModule = require('./myModule.js');
//do something

CMD 推崇依賴就近,在書寫代碼的過程中再根據(jù)其所需要的依賴 require 進(jìn)來。

AMD 與 CMD 對于依賴的模塊都是異步加載。 其最大的區(qū)別是對依賴模塊的執(zhí)行時機處理不同。

AMD 依賴前置,瀏覽器會立即加載其依賴模塊;而 CMD 是依賴就近,需要將模塊轉(zhuǎn)為字符串解析才能確認(rèn)其依賴模塊并加載,這是一種犧牲性能來帶來開發(fā)的便利性的做法。

UMD(Universal Module Definition)

UMD 即通用模塊定義,是一種基本上可以在任何一個模塊環(huán)境中工作的規(guī)范。

一段典型的 UMD 代碼如下所示

(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : // CMD
    typeof define === 'function' && define.amd ? define(['exports'], factory) : // AMD
      (factory((global['module'] = {}))); // Browser globals
}(this, (function (exports) {
  'use strict';

  exports.x = x;
  Object.defineProperty(exports, '__esModule', { value: true });
})));

其原理是通過對不同的環(huán)境的判斷做相應(yīng)的處理。

ES6 模塊

在 ES6 中,JavaScript 終于有了自己真正模塊的概念。ES6 在語言標(biāo)準(zhǔn)的層面上,實現(xiàn)了模塊功能,而且實現(xiàn)得相當(dāng)簡單,完全可以取代之前的規(guī)范,成為瀏覽器和服務(wù)器通用的模塊解決方案。

在 ES6 模塊系統(tǒng)中,通過 export 和 export default 命令規(guī)定模塊的對外接口,import 命令輸入模塊提供的功能。

myModule.js 中

function add(x, y) {
  return x + y;
}
function minus(x, y) {
  return x - y;
}

export {
  add,
  minus
}

main.js 中

import { add, minus } from './myModules.js';

console.log(add(1, 2)) // 3
console.log(minus(2, 1)) // 1

對于 ES6 的模塊在此不多做介紹,參考文檔見阮一峰大神的《 ECMAScript 6 入門 》。

寫在最后

在 JavaScript 模塊化的探索道路上,出現(xiàn)了很多優(yōu)秀的規(guī)范與框架,除了上述具有代表性的規(guī)范外,還有像 YUI、KMD 等其它優(yōu)秀的規(guī)范框架。

雖然說 JavaScript 的模塊化發(fā)展史(更確切的應(yīng)該是 JavaScript 發(fā)展史)一路充滿艱辛坎坷,但其實我們可以看到它正走在正確的道路上,越來越好,衷心希望未來 JavaScript 能夠越來越強大,能夠讓我們在未來遇見更多的可能。

參考資料

JavaScript模塊的前世今生

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容