核心:commonJS是运行时导入,es6是编译时导入
理解:
两个文件a.js导出 b.js导入
- CommonJS
#a.js
let a = 0;
const count = () => {
a++;
}
setTimeout(function(){
a++;
console.log('in module the a is ' + a);
}, 500);
module.exports = {
a,
count,
};
#b.js
let foo = require('./a.js');
foo.count();
setTimeout(function(){
console.log('in require the a is ' + foo.a);
}, 1000);
运行b.js后流程如下:
- 运行到第一行,运行a.js文件代码,所有代码的执行都是在a.js作用域下,至module.exports,将module.exports对象引用传递给foo,此时的a为0,因此b.js中foo.a为0
- 运行到第二行,require赋值给foo时,采用的是基本类型值是值复制赋值,对象类型是引用赋值,因此调用foo.count时,由于引用赋值,foo.count的运行环境也在a.js作用域下,然后更改a为1
- 此时第一步中a.js运行至500ms定时器触发,2步骤中将a.js作用域下的a的值改成了1,而后定时器函数将a自增为2,并且打印输出
in module the a is 2 - 运行至第三行,定时器触发,foo.a是值赋值的原因,在require也就是第一行运行时就已经定死了,所以此时打印输出
in require the a is 0
相当于将a.js代码贴至require处进行一个局部作用域封装,将foo=module.exports:(具体实现估计不是这样,这儿只是等价于)
let foo//------------------------
(function(){
let a = 0;
const count = () => {
a++;
}
setTimeout(function(){
a++;
console.log('in module the a is ' + a);
}, 500);
foo = {//------------------------
a,
count,
};
})()//封装成局部作用域,所以可以理解成require为运行时,遇到则运行后面跟着的模块代码到module.exports,将当前的状态值复制、引用赋值给前面的接收参数
foo.count();
setTimeout(function(){
console.log('in require the a is ' + foo.a);
}, 1000);
- ES6
tips:这儿的mjs是为了不配babel或者一些es6解析器解析es6语法,直接使用node命令运行b.mjs文件加上--experimentabl-modules参数命令
node .\b.mjs --experimentabl-modules这样可以直接运行es6模板文件
#a.mjs
let a = 0;
const count = () => {
a++;
}
count()
setTimeout(function(){
a++;
console.log('in module the a is ' + a);
}, 500);
export {
a,
count,
};
#b.mjs
import { a as b, count } from './a.mjs';
count();
console.log('import a: '+ b);
setTimeout(function(){
console.log('in require the a is ' + b);
}, 1000);
运行b.mjs后流程如下:
- import代码处直接将a.mjs代码编译进b.mjs
- 运行第一行,将import的接收参数(涉及重命名啥的)全部指向./a.mjs中的各个对应变量的地址,所以此时b就是指向的a地址,实际上就是a,count就是count
- 运行第二行,将a+1=1
- 运行第三行,打印输出
import a: 1 - 运行500ms的定时器,a+1=2
- 运行1000ms定时器,打印输出
in require the a is 2
相当于:(具体实现估计不是这样,这儿只是等价于)
let a = 0;
const count = () => {
a++;
}
count()
setTimeout(function(){
a++;
console.log('in module the a is ' + a);
}, 500);
count();
console.log('import a: '+a);//b就是a,相当于编译成了a
setTimeout(function(){
console.log('in require the a is ' + a);//b就是a,相当于编译成了a
}, 1000);
区别
-
commonJS的require是运行时导入,将运行到module.exports的结果赋值给导入的对象**(经过测试,写在module.exports之后的修改a值的代码不会在赋值时生效,会在赋值完毕之后运行)**,涉及到的基本类型变量采用值复制赋值,涉及到的对象变量采用引用赋值
-
es6的import是编译时导入,导入的对象不管是基本类型还是对象变量都是指向导出变量的地址
总结
不管import还是require,运行当前文件时,被导入的模块文件里的代码全部都会执行
es6导入的基本类型和对象就是指向的原始地址,commonJS导入的基本类型值是运行时值(不是所有异步操作完毕后的值),对象变量指向的原始地址
主要就是涉及到基本类型变量的更改时,使用require时会出问题