lovelmh13/myBlog

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 的模块化 thisundefined,CommonJS 是 当前模块

参考

Module 的加载实现 阮一峰
require,import区别? 寸志知乎回答