ES6的模块化与 CommonJS ,Node 的模块化与 CommonJS 的区别是什么
Opened this issue · 0 comments
看了一些相关的文章,大致就是一下几点:
1. es6模块化是值的引用,而CommonJS获取的是值的拷贝。
一提到拷贝就明白了,就是不管被引用的的文件里的值怎么变,都不会影响到引入文件的这个文件里面的变化。 说的有的拗口,上代码看一下:
// counter.js
exports.count = 0
setTimeout(function () {
console.log('increase count to', ++exports.count, 'in counter.js after 500ms')
}, 500)
// commonjs.js
const {count} = require('./counter')
setTimeout(function () {
console.log('read count after 1000ms in commonjs is', count) // read count after 1000ms in commonjs is 0
}, 1000)
//es6.js
import {count} from './counter'
setTimeout(function () {
console.log('read count after 1000ms in es6 is', count) // read count after 1000ms in es6 is 1
}, 1000)
一下子就看出区别来了
2. es6模块化是编译时输出,CommonJS是运行时加载
可能一下也理解不了。这里说一下这一点导致的影响吧。由于,es6
的模块化是编译时输出的,所以我们要在文件开头就声明。而CommonJS
是运行时才加载,所以我们可以随时随地的用require
来加载模块。并且 CommonJS
是同步阻塞的加载的,当执行到 require
的时候,就会去执行 require
的文件。但是如果这个模块已经被加载过,存在缓存列表里了的话,就会去读缓存。
所以 ES6 的模块化可以用 Tree-shaking
// main.js
const a = require('./a');
console.log('in main, a.a1 = %j, a.a2 = %j', a.a1, a.a2);
// a.js
exports.a1 = true;
const b = require('./b.js');
console.log('in a, b.done = %j', b.done);
exports.a2 = true;
// b.js
const a = require('./a.js');
console.log('in b, a.a1 = %j, a.a2 = %j', a.a1, a.a2);
执行结果:
in b, a.a1 = true, a.a2 = undefined
in main, a.a1 = true, a.a2 = true
具体的可以参看:CommonJS 和 ES6 Module 究竟有什么区别? 的「缓存和循环引用」部分。或者 Node 文档 CommonJS 模块
ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。换句话说,ES6 的import有点像 Unix 系统的“符号连接”,原始值变了,import加载的值也会跟着变。因此,ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。
由于 ES6 输入的模块变量,只是一个“符号连接”,所以这个变量是只读的,对它进行重新赋值会报错。
// lib.js
export let obj = {};
// main.js
import { obj } from './lib';
obj.prop = 123; // OK
obj = {}; // TypeError
上面代码中,main.js从lib.js输入变量obj,可以对obj添加属性,但是重新赋值就会报错。因为变量obj指向的地址是只读的,不能重新赋值,这就好比main.js创造了一个名为obj的const变量。
最后,export通过接口,输出的是同一个值。不同的脚本加载这个接口,得到的都是同样的实例。
// mod.js
function C() {
this.sum = 0;
this.add = function () {
this.sum += 1;
};
this.show = function () {
console.log(this.sum);
};
}
export let c = new C();
上面的脚本mod.js,输出的是一个C的实例。不同的脚本加载这个模块,得到的都是同一个实例。
// x.js
import {c} from './mod';
c.add();
// y.js
import {c} from './mod';
c.show();
// main.js
import './x';
import './y';
现在执行main.js,输出的是1。
$ babel-node main.js
1
这就证明了x.js和y.js加载的都是C的同一个实例。
3. es6的模块化不能直接用,CommonJS可以直接用
es6的模块化需要被编译才能被浏览器使用
4. 自动开启严格模式
es6 模块脚本自动采用严格模式,不管有没有声明use strict。
es6 的模块化 this
是 undefined
,CommonJS 是 当前模块