您好,登錄后才能下訂單哦!
CommonJS模塊規范使用require語句導入模塊,module.exports導出模塊,輸出的是值的拷貝,模塊導入的也是輸出值的拷貝,也就是說,一旦輸出這個值,這個值在模塊內部的變化是監聽不到的。
ES6模塊的規范是使用import語句導入模塊,export語句導出模塊,輸出的是對值的引用。ES6模塊的運行機制和CommonJS不一樣,遇到模塊加載命令import時不去執行這個模塊,只會生成一個動態的只讀引用,等真的需要用到這個值時,再到模塊中取值,也就是說原始值變了,那輸入值也會發生變化。
那CommonJS和ES6模塊規范針對模塊的循環加載處理機制有什么不同呢?
循環加載指的是a腳本的執行依賴b腳本,b腳本的執行依賴a腳本。
1. CommonJS模塊的加載原理
CommonJS模塊就是一個腳本文件,require命令第一次加載該腳本時就會執行整個腳本,然后在內存中生成該模塊的一個說明對象。
{ id: '', //模塊名,唯一 exports: { //模塊輸出的各個接口 ... }, loaded: true, //模塊的腳本是否執行完畢 ... }
以后用到這個模塊時,就會到對象的exports屬性中取值。即使再次執行require命令,也不會再次執行該模塊,而是到緩存中取值。
CommonJS模塊是加載時執行,即腳本代碼在require時就全部執行。一旦出現某個模塊被“循環加載”,就只輸出已經執行的部分,沒有執行的部分不會輸出。
案例說明:
案例來源于Node官方說明: https://nodejs.org/api/modules.html#modules_cycles
//a.js exports.done = false; var b = require('./b.js'); console.log('在a.js中,b.done = %j', b.done); exports.done = true; console.log('a.js執行完畢!')
//b.js exports.done = false; var a = require('./a.js'); console.log('在b.js中,a.done = %j', a.done); exports.done = true; console.log('b.js執行完畢!')
//main.js var a = require('./a.js'); var b = require('./b.js'); console.log('在main.js中,a.done = %j, b.done = %j', a.done, b.done);
輸出結果如下:
//node環境下運行main.js
node main.js在b.js中,a.done = false
b.js執行完畢!
在a.js中,b.done = true
a.js執行完畢!
在main.js中,a.done = true, b.done = true
JS代碼執行順序如下:
1)main.js中先加載a.js,a腳本先輸出done變量,值為false,然后加載b腳本,a的代碼停止執行,等待b腳本執行完成后,才會繼續往下執行。
2)b.js執行到第二行會去加載a.js,這時發生循環加載,系統會去a.js模塊對應對象的exports屬性取值,因為a.js沒執行完,從exports屬性只能取回已經執行的部分,未執行的部分不返回,所以取回的值并不是最后的值。
3)a.js已執行的代碼只有一行,exports.done = false;所以對于b.js來說,require a.js只輸出了一個變量done,值為false。往下執行console.log('在b.js中,a.done = %j', a.done);控制臺打印出:
在b.js中,a.done = false
4)b.js繼續往下執行,done變量設置為true,console.log('b.js執行完畢!'),等到全部執行完畢,將執行權交還給a.js。此時控制臺輸出:
b.js執行完畢!
5)執行權交給a.js后,a.js接著往下執行,執行console.log('在a.js中,b.done = %j', b.done);控制臺打印出:
在a.js中,b.done = true
6)a.js繼續執行,變量done設置為true,直到a.js執行完畢。
a.js執行完畢!
7)main.js中第二行不會再次執行b.js,直接輸出緩存結果。最后控制臺輸出:
在main.js中,a.done = true, b.done = true
總結:
1)在b.js中,a.js沒有執行完畢,只執行了第一行,所以循環加載中,只輸出已執行的部分。
2)main.js第二行不會再次執行,而是輸出緩存b.js的執行結果。exports.done = true;
2. ES6模塊的循環加載
ES6模塊與CommonJS有本質區別,ES6模塊是動態引用,遇到模塊加載命令import時不會去執行模塊,只是生成一個指向被加載模塊的引用,需要開發者保證真正取值時能夠取到值,只要引用是存在的,代碼就能執行。
案例說明:
//even.js import {odd} from './odd'; var counter = 0; export function even(n){ counter ++; console.log(counter); return n == 0 || odd(n-1); }
//odd.js import {even} from './even.js'; export function odd(n){ return n != 0 && even(n-1); } //index.js import * as m from './even.js'; var x = m.even(5); console.log(x); var y = m.even(4); console.log(y);
執行index.js,輸出結果如下:
babel-node index.js
1
2
3
false
4
5
6
true
可以看出counter的值是累加的,ES6是動態引用。如果上面的引用改為CommonJS代碼,會報錯,因為在odd.js里,even.js代碼并沒有執行。
//改用CommonJS規范加載文件,執行會報錯 var x = m.even(5); ^ TypeError: m.even is not a function at Object.<anonymous> (/Users/zourong/Projects/node/ES6/mainx.1.js:3:11) at Module._compile (internal/modules/cjs/loader.js:689:30)
3. 總結
1)CommonJS模塊是加載時執行。一旦出現某個模塊被“循環加載”,就只輸出已經執行的部分,沒有執行的部分不會輸出。
2)ES6模塊是動態引用,遇到模塊加載命令import時不會去執行模塊,只是生成一個指向被加載模塊的引用。
CommonJS模塊規范主要適用于后端Node.js,后端Node.js是同步模塊加載,所以在模塊循環引入時模塊已經執行完畢。推薦前端工程中使用ES6的模塊規范,通過安裝Babel轉碼插件支持ES6模塊引入的語法。
頁面內容主要來源于《ES6標準入門》Module 這一章的介紹。如果有描述不清楚或錯誤的地方,歡迎留言指證。
參考資料:
《ES6標準入門》之Module
Node.js Cycle
ES-Module-Loader
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。