ES6 : Module的加載
ES6子產品與CommonJS子產品的差異
- CommonJS輸出的是一個值的複制值,ES6子產品輸出的是一個值的引用,也就是說,對CommonJS子產品輸出的值進行更改,不會影響到其子產品本身的值,而ES6則會影響對同一個子產品引用的值。
- CommonJS是運作時加載,ES6是編譯時輸出接口。
import指令加載CommonJS子產品
- 在Node環境中,使用import指令加載CommonJS子產品,Node将會自動将module.exports屬性當作子產品的預設輸出,等同于export default。
// a.js module.exports = { foo : "hello" } // 等同于 module export { foo : "hello" } // 引入 import baz from './a'; import {default as baz} from './a'; // baz === { foo : "hello"};
- 如果采用整體輸入的方法,default将會取代module.exports作為輸入的接口
```js
import * as baz from './a';
// baz === {
// get default(){
// return module.exports;
// },
// get foo(){
// return {this.default.foo}.bind(baz);
// }
//}
- 上面的例子中,this.default代替了module.exports,同時也不難看出,import文法會給baz添加default屬性。
require指令加載ES6子產品
- 采用require加載ES6子產品時,ES6子產品的所有輸出接口都會成為輸入對象的屬性
// a.js let foo = { bar : "hello" } export default foo; // b.js const amodule = require('./a.js'); // amodule.default === {bar : "hello"}
循環加載
- 所謂循環加載,就是兩個子產品互相引用,導緻程式遞歸執行,而兩種子產品對此的處理方法不同。
CommonJS的循環加載
- CommonJS的子產品加載原理:cjs子產品無論被加載多少次,都隻會在第一次加載的時候運作一次,以後加載的都是第一次傳回的結果,除非手動清除緩存;
// a.js export.done = false; let b = require('./b'); console.log('在a.js中,b.done = %j',b.done); export done = true; console.log('在a.js中,b.done = %j',b.done); console.log('a.js執行完畢'); // b.js export.done = false; let a = require('./a'); console.log('在b.js中,a.done = %j',a.done); export done = true; console.log('在b.js中,a.done = %j',a.done); console.log('b.js執行完畢');
- 在上面的代碼中,a.js執行到第二行就會去執行b.js,而b.js執行到第二行就會去執行a.js,系統會自動去a.js尋找對應的export值,但是a.js還沒有執行完畢,是以,從exports值隻能取出已經執行了的值。
// main.js var a = require('./a'); var b = require('./b'); console.log('在main.js中,a.done = %j,b.done = %j',a.done,b.done); // 輸出 在b.js中,a.done = false; b.js執行完畢; 在a.js中,b.done = true; a.js執行完畢; 在main.js中,a.done = true,b.done = true;
- 以上代碼證明,main檔案執行到第二行是不會再重新執行一次b.js,而是直接傳回它最終的值。
ES6子產品的循環加載
- 由于ES6子產品為動态引用,變量不會緩存,而是指向引用的對象。
// a.js import {bar} from "./b.js"; console.log("a.js"); console.log(bar); export let foo = 'foo'; // b.js import {foo} from "./a.js"; console.log("b.js"); console.log(foo); export let bar = "bar"; // 執行a.js b.js undefined a.js bar
- 以上代碼,a.js執行,第一行代碼引用b.js,是以運作b.js,而b.js的第一行代碼是引入a.js,由于a.js已經運作,是以不會再加載一次,是以輸出了undefined,随後正常執行。
兩種模式的比較
// a.js
import {bar} from "./b.js"
export function foo(){
console.log('foo');
bar();
console.log('執行完畢');
}
foo();
// b.js
import {foo} from "./a.js"
export function bar(){
console.log('bar');
if(Math.random() > 0.5){
foo();
}
}
- 以上這段代碼,在CommonJS的标準下是無法執行的。a加載b,然後b又加載a,此時a還未執行結束,是以輸出的結果将為null,即對b的foo的值為
,是以無法執行,将會報錯;null
- 而在ES6标準下,由于import建立的是對象的引用,是以在a加載時,第一行代碼建立了對b.js的
的引用,運作到第四行代碼時,将會跳到b.js中執行bar
;随後再執行下一行代碼的console。bar()