Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ES6的模块化与 CommonJS ,Node 的模块化与 CommonJS 的区别是什么 #33

Open
lovelmh13 opened this issue May 4, 2020 · 0 comments

Comments

@lovelmh13
Copy link
Owner

lovelmh13 commented May 4, 2020

看了一些相关的文章,大致就是一下几点:

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区别? 寸志知乎回答

@lovelmh13 lovelmh13 changed the title ES6的模块化与 CommonJS ES6的模块化与 CommonJS ,Node 的模块化与 CommonJS 的区别是什么 May 30, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant