咱們都知道 JavaScript 中並無模塊的概念,一開始 JavaScript 的出現只是做爲簡單腳本語言來實現簡單的頁面邏輯,而隨着互聯網的發展和 web 2.0 時代的到來,前端代碼呈現井噴式發展,隨着代碼量的增長,模塊缺失的問題日益凸顯,而同時 JavaScript 社區也作了不少探索。javascript
那麼什麼是模塊呢?前端
模塊,是指可以單獨命名並獨立地完成必定功能的程序語句的集合(即程序代碼和數據結構的集合體)。java
因此,模塊的核心就是須要完成特定的功能,而且其很重要的一點就是須要解決引用依賴以及被依賴的問題。es6
從定義上來講,其實函數也能夠被看成是一個模塊。web
在 myModule.js 中新增以下函數編程
function add(x, y) {
return x + y;
}
function minus(x, y) {
return x - y;
}
複製代碼
經過 script 標籤引入 myModule.js 後可直接使用裏面的函數數組
<script src="myModule.js"></script>
<script>
console.log(add(1, 2)) // 3
console.log(minus(2, 1)) // 1
</script>
複製代碼
在 myModule.js 中,每個函數均可以被認爲是一個模塊。經過函數方式定義模塊主要有兩個缺陷:一是會污染全局變量,沒法保證各模塊間的變量名不衝突;二是須要手動維護依賴的順序,若是 myModule.js 中的模塊須要依賴其它模塊(如 jQuery),則該模塊(jQuery)須要在 myModule.js 以前引用。瀏覽器
自 2009 年 NodeJS 誕生後,JavaScript 模塊化編程正式進入人們的視野,而 NodeJS 的模塊化系統,即是參照 CommonJS 規範實現的。bash
在 CommonJS 規範中,一個文件就是一個模塊,每一個模塊都有各自的做用域,即在一個模塊中的變量、函數是私有的,外部沒法訪問。服務器
在模塊內部,module 變量對象表明當前模塊,其 exports 屬性也是一個對象,表明對外的接口,因此咱們將須要對外暴露的內容放進 module.exprots 對象便可;而引用模塊則使用 require 函數。
使用 CommonJS 規範來定義 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 規範中,require 加載模塊的方式是同步加載,使得其不適合在瀏覽器端使用。在服務器端同步加載模塊,等待時間取決於硬盤的讀取時間,而在瀏覽器端同步加載模塊,等待時間取決於網速快慢,這使得在等待加載模塊的過程當中,瀏覽器會處於假死的狀態。
AMD 即異步模塊定義,是 RequireJS 在推廣過程當中對模塊定義的規範化產出。一樣的,其規定一個文件就是一個模塊,文件名即模塊名。
AMD 使用 define 函數定義模塊;使用 require 函數引用模塊。
define 函數使用方式以下
define(id?, dependencies?, factory);
複製代碼
定義 myModule.js 模塊
define(['depenModule'], function (depenModule) {
//do something
});
複製代碼
require 接收兩個參數,第一個參數爲所依賴的模塊標識數組;第二個參數爲依賴模塊加載完成後的回調函數。
在 main.js 中引用 myModule 模塊
require(['myModule'], function (myModule){
//do something
});
複製代碼
AMD 推崇依賴前置,須要先異步加載其所需的依賴模塊後纔會執行相應回調函數中的代碼。
CMD 即公共模塊定義,是 SeaJS 在推廣過程當中對模塊定義的規範化產出。一樣的,其規定一個文件就是一個模塊,文件名即模塊名。
其使用 define 函數定義模塊;使用 require 函數引用模塊。
define 函數使用方式以下
define(factory)
複製代碼
定義 myModule.js 模塊
define(function(require, exports, module) {
//do something
});
複製代碼
在 main.js 中引用 myModule 模塊
var myModule = require('./myModule.js');
//do something
複製代碼
CMD 推崇依賴就近,在書寫代碼的過程當中再根據其所須要的依賴 require 進來。
AMD 與 CMD 對於依賴的模塊都是異步加載。 其最大的區別是對依賴模塊的執行時機處理不一樣。
AMD 依賴前置,瀏覽器會當即加載其依賴模塊;而 CMD 是依賴就近,須要將模塊轉爲字符串解析才能確認其依賴模塊並加載,這是一種犧牲性能來帶來開發的便利性的作法。
UMD 即通用模塊定義,是一種基本上能夠在任何一個模塊環境中工做的規範。
一段典型的 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 });
})));
複製代碼
其原理是經過對不一樣的環境的判斷作相應的處理。
在 ES6 中,JavaScript 終於有了本身真正模塊的概念。ES6 在語言標準的層面上,實現了模塊功能,並且實現得至關簡單,徹底能夠取代以前的規範,成爲瀏覽器和服務器通用的模塊解決方案。
在 ES6 模塊系統中,經過 export 和 export default 命令規定模塊的對外接口,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 模塊化的探索道路上,出現了不少優秀的規範與框架,除了上述具備表明性的規範外,還有像 YUI、KMD 等其它優秀的規範框架。
雖說 JavaScript 的模塊化發展史(更確切的應該是 JavaScript 發展史)一路充滿艱辛坎坷,但其實咱們能夠看到它正走在正確的道路上,愈來愈好,衷心但願將來 JavaScript 可以愈來愈強大,可以讓咱們在將來碰見更多的可能。
公衆號不定時分享我的在前端方面的學習經驗,歡迎關注。