CommonJS与ES6模块

294 阅读2分钟

CommonJS与ES6区别

  • 1、ES6模块输出的是值的引用,而CommonJS模块输出的是值的拷贝
  • 2、ES6模块是在编译时执行,而CommonJS模块是在运行时加载

CommonJS模块输出值的拷贝

// a.js
var b = require('./b');
console.log(b.foo);
setTimeout(() => {
    console.log(b.foo);
    console.log(require('./b').foo);
}, 1000);

// b.js
let foo = 1;
console.log('b.js')
setTimeout(() => {
    foo = 2;
}, 500);
module.exports = {
    foo: foo,
};

//b.js
//1
//1
//1

b 模块加载以后,它的内部 foo 变化就影响不到输出的 exports.foo 了。这是因为 foo 是一个原始类型的值,会被缓存。所以如果你想要在 CommonJS 中动态获取模块中的值,那么就需要借助于函数延时执行的特性。

// b.js
let foo = 1;
setTimeout(() => {
  foo = 2;
}, 500);
module.exports = {
  foo: () => { //返回一个方法
    return foo;
  },
};

总结:

  • 1、CommonJS 模块重复引入的模块并不会重复执行,再次获取模块直接获得暴露的 module.exports 对象
  • 2、如果你要处处获取到模块内的最新值的话,也可以你每次更新数据的时候每次都要去更新 module.exports 上的值
  • 3、如果你暴露的 module.exports 的属性是个对象,那就不存在这个问题了

ES6模块输出引用

执行node --experimental-modules a.mjs

//a.mjs
import {foo}  from './b';
console.log(foo);
setTimeout(() => {
    console.log(foo);
    import('./b').then(({foo})=>{
        console.log(foo);
    });
}, 1000);

// b.mjs
console.log('b.js')
setTimeout(() => {
    foo = 2;
}, 500);

export let foo = 1;

//b.js
//1
//2
//2

在 ES6 模块中就不再是生成输出对象的拷贝,而是动态关联模块中的值

ES6静态编译

ES6 模块编译时执行会导致有以下两个特点:

  • 1、import 命令会被 JavaScript 引擎静态分析,优先于模块内的其他内容执行
  • 2、export 命令会有变量声明提前的效果

import 优先执行:

//a.mjs
console.log('a.mjs');
import { foo } from './module2';

// b.mjs
export let foo = 1;
console.log('b.mjs');

//b.mjs
//a.mjs

虽然 a 模块中 import 引入晚于 console.log('a.mjs'),但是它被 JS 引擎通过静态分析,提到模块执行的最前面,优于模块中的其他部分的执行。由于 import 是静态执行,所以 import 具有提升效果即 import 命令在模块中的位置并不影响程序的输出。

export 变量声明提升:

//a.mjs
console.log('a.mjs');
import { foo } from './module2';
export const bar = 1;
export const bar2 = () => {
    console.log('bar2');
}
export function bar3() {
    console.log('bar3');
}

// b.mjs
export let foo = 1;
import * as a from './module';
console.log(a);

[Module] {
  bar: <uninitialized>,
  bar2: <uninitialized>,
  bar3: [Function: bar3] }
a.mjs

a 模块引用了 b 模块,b 模块也引用了 a 模块,export 声明的变量也是优于模块其它内容的执行的,但是具体对变量赋值需要等到执行到相应代码的时候。

动态 import()

import() 主要是为了解决 ES6 模块无法在运行时确定模块的引用关系,所以需要引入 import()

参考文章

深入理解 ES6 模块机制