ES6模块与CommonJS模块的区别


1.写法不同

最直观的,两者对于模块的导入导出写法不同

  • ES6
1
2
3
4
5
6
7
8
/* 导出 */
export let a = 10
export let b = 20
export default {...}

/* 导入 */
import {a, b} from './test.js'
import c from './test.js'
  • CommonJS
1
2
3
4
5
6
/* 导出 */
module.exports = { a: 10, b: 20 };

/* 导入 */
let test = require("./test.js");
console.log(test.a, test.b); // 输出 10 20

注意:上述 CommonJS 导出写法会改变原本指向的内存,使其与 exports 指向不同内存空间,语法非本篇重点,可看这篇博客

https://www.cnblogs.com/fightjianxian/p/12151010.html


2.加载阶段不同

  • CommonJS 模块加载的是对象(即module.exports属性),它只有在脚本运行完才会生成,所以是运行时加载
  • 而 ES6 模块不是对象,它的对外接口是一种静态定义,在代码静态解析阶段就会生成,所以是编译时输出接口

3.输出内容不同

  • 刚刚说了 CommonJS 模块输出的是一个对象,所以是一个值的拷贝(浅拷贝)

请看下面这个例子,模块内有一个变量 a 和一个自增函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* 模块test.js */
let a = 1;
function add() {
a++;
}
module.exports = {
a: a,
add: add,
};
/* 需要引入模块的文件main.js */
let mod = require("./test.js");
console.log(mod.a); // 1
mod.add();
console.log(mod.a); // 1

上述代码说明,test.js模块加载以后,它内部的变化影响不到已经输出的mod.a了,因为 a 会被缓存(a 是一个简单数据类型,导出时直接拷贝值)。除非写成一个函数,才能获得内部变动后的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* 模块test.js */
let a = 1;
function add() {
a++;
}
module.exports = {
get a() {
return a;
},
add: add,
};
/* 需要引入模块的文件main.js */
let mod = require("./test.js");
console.log(mod.a); // 1
mod.add();
console.log(mod.a); // 2

上面代码中,输出的a实际上是一个取值器函数。如果想输出后的a发生改变,可以这样修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* 模块test.js */
let a = 1;
function add() {
this.a++;
}
module.exports = {
a: a,
add: add,
};
/* 需要引入模块的文件main.js */
let mod = require("./test.js");
console.log(mod.a); // 1
mod.add();
console.log(mod.a); // 2

这样改变的是输出的对象的a属性,模块内部并未受影响

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* 模块test.js */
let a = 1;
function add() {
this.a++;
console.log(a); // 1
}
module.exports = {
a: a,
add: add,
};
/* 需要引入模块的文件main.js */
let mod = require("./test.js");
mod.add();
console.log(mod.a); // 2

这或许对你理解CommonJS模块输出的是一个对象有所帮助

  • 而 ES6 模块输出的是值的引用

ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。同样的代码

1
2
3
4
5
6
7
8
9
10
/* 模块test.js */
export let a = 1;
export function add() {
a++;
}
/* 需要引入模块的文件main.js */
import { a, add } from "./test.js";
console.log(a); // 3
add();
console.log(a); // 4

上述代码说明 ES6 模块输出的变量a完全反应其在所在模块内部的变化


4.加载方式不同

  • CommonJS 模块的require()是同步加载模块
  • ES6 模块的import命令是异步加载,有一个独立的模块依赖的解析阶段

参考自阮一峰老师的博客

https://es6.ruanyifeng.com/#docs/module-loader#ES6-%E6%A8%A1%E5%9D%97%E4%B8%8E-CommonJS-%E6%A8%A1%E5%9D%97%E7%9A%84%E5%B7%AE%E5%BC%82