ES6:模块的加载,与CommonJS进行比较

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。

猜你喜欢

转载自blog.csdn.net/yivisir/article/details/109372174