-
<b>CommonJS規范</b>
由于ES5沒有模塊化規范,所以產生了這三種規范。在ES6中又新增了一種公用模塊化的方法。
特點:同步
CommonJS規范是通過module.exports定義的,在前端瀏覽器中并不支持此中規范
瀏覽器不兼容Common的根本原因也就是缺少四個Node環境的變量
- module
- exports
- require
- global
Node以及Webpack是采用CommonJS的形式來寫的
CommonJS定義的模塊分為三種:模塊引用(require);模塊定義(exports);模塊標識(module)
require()用來引入外部模塊,exports對象用于導出當前模塊,或者當前的模塊的方法和變量,module對象代表對象本身
var foo = require('foo.js'); var count =1; var plus = ()=>{ foo.add(count); }; module.exports= { count, plus }
<b>特點</b>
? 對于基本數據類型,和語言本身一樣屬于復制,即會被模塊緩存,在另一個模塊中可以對該模塊輸出的變量重新賦值。
? 對于復雜數據類型,例如Array,Object,屬于淺拷貝,即同時指向一個內存空間,因此對一個模塊的值的改變可以影響另一個模塊。詳見javascript深拷貝與淺拷貝詳解。
當使用require命令加載同一個模塊時,不會再執行該模塊,而是取到緩存之中的值。也就是說,CommonJS模塊無論加載多少次,都只會在第一次加載時運行一次,以后再加載,就返回第一次運行的結果,除非手動清除系統緩存。
<b>循環加載</b>
// a.js exports.done = false let b = require('./b.js') console.log('a.js-1', b.done) exports.done = true console.log('a.js-2', '執行完畢') // b.js exports.done = false let a = require('./a.js') console.log('b.js-1', a.done) exports.done = true console.log('b.js-2', '執行完畢') // c.js let a = require('./a.js') let b = require('./b.js') console.log('c.js-1', '執行完畢', a.done, b.done)
運行node c.js
usr:~ usr$ node c.js b.js-1 false b.js-2 執行完畢 a.js-1 true a.js-2 執行完畢 c.js-1 執行完畢 true true
循環加載時,commonjs屬于加載時執行,即腳本代碼在require時候,就會全部執行。一旦出現某個模塊被“循環加載”,只輸出已經執行的部分,未執行的部分不輸出。
上述代碼很明顯的表現出了此類特點
step1: node c.js
step2: require(./a.js) //執行完a.js中的內容
step2.1: export.done = flase; let b = require(./b.js); // 執行b中的代碼
step2.2: export.done = flase; let a= require(./a.js) //由于發生了循環加載,所以只執行a中的第一句,然后繼續執行b.js 輸出 <code>b.js-1 false b.js-2執行完畢</code>
step2.3: 繼續執行b中的代碼 將b.done 賦值為true
step 2.4 : 執行a的剩余部分
a.js-1 true; a.js-2 執行完畢
step3:require(./b.js) 由于已經執行過了不會再執行b中的代碼 所以直接輸出b返回值
step4: 輸出
c.js-1 執行完畢 true true
-
<b>AMD規范</b>
由于CommonJS的局限性 例如
var math = require('math'); math.add(2,3);
第二行代碼必須要在 require之后運行,如果math加載時間很長,就會陷入空等,整個過程是同步的。
對于服務器這不是個問題,因為都存儲在本地,但是對于客戶端,如果由于網絡原因,可能會陷入“假死”狀態。
所以客戶端,不能采用同步加載,只能使用異步加載。
AMD規范是require.js對模塊化定義的一種規范化產出
? 模塊本身和模塊之間的引用可以被異步加載
? 先引入模塊后使用模塊的方法,稱之為<b>依賴前置</b>
優點
? 1.包括異步調用和本身高擴展性
? 2.解耦,模塊在代碼中也可以通過識別號進行查找
define(['./package/lib.js'], function(lib) { function say(){ lib.log('this is fn'); } return { say:say }; });
Tips
? lib.js是我們引入的方法,回調中lib參數表示的是引入的模塊的所有方法及屬性
? 我們在當前模塊定義了say方法,并且return say的執行結果
-
<b>CMD規范</b>
SeaJs
CMD:同步模塊定義
依賴就近原則
sayHello.js
define(function(require,exports,module){ //通過require引入依賴,并不是AMD的依賴前置,而是依賴就近,哪里用,哪里引 //例如下列引入jquery var JQ = require('jquery'); exports.sayHello = function(){ $('#hello').toggle('slow') } })
main.js
define(function(require){ var CMD = require('sayHello'); var temp = new CMD(); temp.sayHello(); })
-
<b>ES6模塊化規范</b>
ES6在語言標準上面實現了模塊功能。設計思想是盡量的靜態化,使得編譯時就能確定模塊的依賴關系,以及輸入輸出變量。CommonJS以及AMD都只能在運行時確定
ES6的模塊并不是對象,而是通過export顯示指定輸出的代碼,再通過import命令導入
//commonJs // CommonJS模塊 let { stat, exists, readFile } = require('fs'); // 等同于 let _fs = require('fs'); let stat = _fs.stat; let exists = _fs.exists; let readfile = _fs.readfile; //創建了一個對象 //ES6 import { stat, exists, readFile } from 'fs';
上面代碼的實質是從
fs
模塊加載 3 個方法,其他方法不加載。這種加載稱為“編譯時加載”或者靜態加載,即 ES6 可以在編譯時就完成模塊加載,效率要比 CommonJS 模塊的加載方式高。當然,這也導致了沒法引用 ES6 模塊本身,因為它不是對象。export輸出的接口,是變量實時的值,與commonjs輸出的是值的緩存不相同
export與import要出現在模塊頂層
如果想要重新命名,需要使用as關鍵字
也可以引入整個模塊
import * as bb from './xxx'
也可以匿名導出 這樣可以給導出的模塊任意指定名字
export default
import anyname from 'xx.js'
JavaScript的四種模塊化規范
最后編輯于 :
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。