Javascript
的模板規範共有兩種:CommonJS
和 AMD
commonjs
nodejs的模塊系統,是參照commonjs
規範實現的javascript
commonjs
即爲服務器端模塊的規範。 commonjs
的規範: 根據commonjs
規範,一個單獨的文件就是一個模塊。加載模塊使用require
方法,該方法讀取一個文件並執行,最後返回文件內部的exports對象java
commonjs
模塊的加載原理commonjs
模塊不管加載多少次,都只會在第一次加載時運行一次,之後再加載,就返回第一次運行的結果,除非手動清除系統緩存。node
AMD
commonjs
規範加載模塊是同步的,也就是說,只有加載完成,才能執行後面的操做。AMD
規範則是非同步加載模塊,容許指定回調函數。因爲Node.js主要用於服務器編程,模塊文件通常都已經存在於本地硬盤,因此加載起來比較快,不用考慮非同步加載的方式,因此commonjs
規範比較適用。可是,若是是瀏覽器環境,要從服務器端加載模塊,這時就必須採用非同步模式,所以瀏覽器端通常採用AMD規範。es6
能夠理解爲AMD
即爲能在客戶端環境,而且能兼容服務器端模塊的一種模塊規範web
AMD
的模塊定義:
AMD規範使用define
方法定義模塊編程
Define第一個參數表達依賴的模塊數組,第二個爲加載完依賴的模塊數組後,模塊執行的函數
AMD
的模塊加載定義:跟commonjs
同樣,AMD
也採用require()
語句來加載模塊,可是與commonjs
不一樣的是,它要求有兩個參數:
第一個參數[module]
,是一個數組,裏面的成員就是要加載的模塊;第二個參數callback,則是加載成功以後的回調函數數組
AMD
和CMD
對比對於依賴的模塊,AMD
是提早執行,CMD
是延遲執行。不過 RequireJS 從 2.0 開始,也改爲能夠延遲執行(根據寫法不一樣,處理方式不一樣)。CMD
推崇 as lazy as possible.瀏覽器
CMD
推崇依賴就近,AMD
推崇依賴前置緩存
AMD
的API
默認是一個當多個用,CMD
的API 嚴格區分,推崇職責單一。好比AMD
裏,require
分全局require
和局部require
,都叫require
。CMD
裏,沒有全局 require
,而是根據模塊系統的完備性,提供seajs.use
來實現模塊系統的加載啓動。CMD
裏,每一個API都簡單純粹。服務器
ES6 Modules
ES6
模塊的設計思想,是儘可能的靜態化,使得編譯時就能肯定模塊的依賴關係,以及輸入和輸出的變量。commonjs
和AMD
模塊,都只能在運行時肯定這些東西。好比,commonjs
模塊就是對象,輸入時必須查找對象屬性。
// CommonJS模塊 let { stat, exists, readFile } = require('fs'); // 等同於 let _fs = require('fs'); let stat = _fs.stat, exists = _fs.exists, readfile = _fs.readfile;
上面代碼的實質是總體加載fs模塊(即加載fs的全部方法),生成一個對象(_fs),而後再從這個對象上面讀取3個方法。這種加載稱爲「運行時加載」,由於只有運行時才能獲得這個對象,致使徹底沒辦法在編譯時作「靜態優化」。
ES6模塊不是對象,而是經過export
命令顯式指定輸出的代碼,輸入時也採用靜態命令的形式。
// ES6模塊 import { stat, exists, readFile } from 'fs';
上面代碼的實質是從fs模塊加載3個方法,其餘方法不加載。這種加載稱爲「編譯時加載」,即ES6
能夠在編譯時就完成模塊加載,效率要比CommonJS
模塊的加載方式高。固然,這也致使了無法引用ES6
模塊自己,由於它不是對象。
因爲ES6
模塊是編譯時加載,使得靜態分析成爲可能。有了它,就能進一步拓寬JavaScript
的語法,好比引入宏(macro)和類型檢驗(type system)這些只能靠靜態分析實現的功能。
除了靜態加載帶來的各類好處,ES6模塊還有如下好處。
再也不須要UMD模塊格式了,未來服務器和瀏覽器都會支持ES6
模塊格式。目前,經過各類工具庫,其實已經作到了這一點。
未來瀏覽器的新API就能用模塊格式提供,再也不必要作成全局變量或者navigator對象的屬性。
再也不須要對象做爲命名空間(好比Math對象),將來這些功能能夠經過模塊提供。
瀏覽器使用ES6
模塊的語法以下。
<script type="module" src="foo.js"></script>
上面代碼在網頁中插入一個模塊foo.js,因爲type屬性設爲module,因此瀏覽器知道這是一個ES6模塊。
Node
的默認模塊格式是CommonJS,目前還沒決定怎麼支持ES6
模塊。因此,只能經過Babel這樣的轉碼器,在Node裏面使用ES6
模塊。
export
優先考慮這種寫法而不是一個一個的export
// profile.js var firstName = 'Michael'; var lastName = 'Jackson'; var year = 1958; export {firstName, lastName, year};
提供對外接口必須在接口名與模塊內部變量之間保持一一對應關係
// 報錯 function f() {} export f; // 正確 export function f() {}; // 正確 function f() {} export {f};
最後,export
命令能夠出如今模塊的任何位置,只要處於模塊頂層就能夠。若是處於塊級做用域內,就會報錯,下一節的import
命令也是如此。這是由於處於條件代碼塊之中,就無法作靜態優化了,違背了ES6
模塊的設計初衷。
import
使用export
命令定義了模塊的對外接口之後,其餘JS文件就能夠經過import
命令加載這個模塊(文件)。
// main.js import {firstName, lastName, year} from './profile'; function setName(element) { element.textContent = firstName + ' ' + lastName; }
上面代碼的import命令,就用於加載profile.js文件,並從中輸入變量。import
命令接受一個對象(用大括號表示),裏面指定要從其餘模塊導入的變量名。大括號裏面的變量名,必須與被導入模塊(profile.js)對外接口的名稱相同。
若是想爲輸入的變量從新取一個名字,import
命令要使用as關鍵字,將輸入的變量重命名。
import { lastName as surname } from './profile';
注意,import
命令具備提高效果,會提高到整個模塊的頭部,首先執行。
ES6
模塊加載的實質ES6
模塊加載的機制,與CommonJS
模塊徹底不一樣。CommonJS
模塊輸出的是一個值的拷貝,而ES6
模塊輸出的是值的引用。ES6
的輸入有點像Unix
系統的「符號鏈接」,原始值變了,import
輸入的值也會跟着變。所以,ES6
模塊是動態引用,而且不會緩存值,模塊裏面的變量綁定其所在的模塊。