天天看點

ES6:子產品的加載,與CommonJS進行比較ES6 : Module的加載

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的

    bar

    的引用,運作到第四行代碼時,将會跳到b.js中執行

    bar()

    ;随後再執行下一行代碼的console。

繼續閱讀