对CommonJS和ES6 模块异同的思考(含循环加载)


目录
  • 前言
  • CommonJS和ES6 模块异同
  • 说一说两者循环加载的处理机制

前言

最近在学习的过程中了解到了一些CommonJS的模块机制,借机总结一下其与ES6模块的异同,本文参考了博客园博主凯斯keith 的文章,也参考了博客园博主forcheng>的文章,详情点击如上链接。

CommonJS和ES6 模块异同

不同

CommonJS

  1. 使用require引入模块,一个文件就是一个模块
  2. 对于模块中简单类型的变量:属于对原变量的复制;在导入该模块的文件里对该变量的修改不会同步到原模块,在原模块里对变量进行修改也不会影响导入该模块的文件。
//a.js文件
let count = 0;
let plusCount = () => {
    count++
};
setTimeout(() => {
    console.log('a.js', count)
}, 1000)

plusCount();//调用函数

module.exports = {//导出模块
    count,
    plusCount
}

//b.js文件
let mod = require('./1.a_commonJS')//导入a模块

console.log('b.js-1', mod.count)//输出b.js-1 0
mod.plusCount()// 调用之后,b模块的count不受影响,a的count发生改变
console.log('b.js-2', mod.count)//输出b.js-2 0
mod.count = 4// 手动对count进行赋值
console.log('b.js-3', mod.count)//输出 b.js-3 4

// 最后还会输出:a.js  1
//在b.js里面调用plusCount函数,前后输出count发现count没有改变,但a里面的count发生了改变
//这就说明了:对于简单数据类型,CommonJS模块传递的是值的复制,对一个值的修改不会影响另外一个值。
  1. 对于复杂类型的变量:属于对原变量的浅拷贝,指向同一个地址空间,对变量的修改会相互影响
  2. CommonJS是加载时执行:遇到require的时候,就会全部执行,生成一个对象存在内存里,然后从这个对象的属性上取需要的值
  3. 如果require多次导入同一个模块,则只执行一次(执行时缓存)

ES6

  1. 导入的值只是原模块的动态只读引用
  2. 对于只读:不允许修改引入的模块的值
  3. 对于动态:原始模块发生变化,import加载的值也会变化
  4. ES6模块是编译时输出接口:遇到import时,输出一个动态只读引用,等到真的要用这个值的时候(运行时),再通过这个引用到模块中取值。

相同

  1. CommonJS和ES6 Module都可以对引?的对象进?赋值,即对对象内部属性的值进行改变。
  2. 遇到多次导入,只执行一次(CommonJS会缓存,ES6不会)

说一说两者循环加载的处理机制

CommonJS循环加载

遇到循环加载时,输出已经执行的部分,没有执行的部分不予输出

//a.js
console.log('a starting');
exports.done = false;
const b = require('./b.js');
console.log('in a, b.done = %j', b.done);
exports.done = true;
console.log('a done');

// b.js
console.log('b starting');
exports.done = false;
const a = require('./a.js');
console.log('in b, a.done = %j', a.done);
exports.done = true;
console.log('b done');

//// main.js
console.log('main starting');
const a = require('./a.js');
const b = require('./b.js');
console.log('in main, a.done = %j, b.done = %j', a.done, b.done);

输出为:

次数采用注释的方式说明了执行顺序,需要耐心一点看

main starting #加载main.js
a.starting #在main.js中开始加载a模块
b.starting #在a.js中开始加载b模块
# 加载b时遇到再次对a的加载,发现循环加载 输出已经执行的部分
#此时还处在b模块中
in b,a.done=false #这是因为上一个代码块的第二行
b done
#返回到a模块接着第四行执行
in a,b.done=true #这是因为上一个代码块的第14行
a done
#返回到main
#遇到对b模块的加载,不再执行(CommonJS机制)
in main,a.done=true,b.done=true#这是因为6和14行

ES6循环加载

ES6处理"循环加载"与CommonJS有本质的不同。ES6根本不会关心是否发生了"循环加载",只是生成一个指向被加载模块的引用,需要开发者自己保证,真正取值的时候能够取到值。