区别
- CommonJS模块输出是一个值的拷贝,ES6模块输出的值是引用,同时会继承当前环境
- CommonJS是运行时候加载,ES6模块的编译的时候输出接口
- CommonJS是require()同步加载模块,ES6模块是import命令是异步加载,有一个独立的模块依赖的解析阶段
CommonJS模块输出是一个值的拷贝,ES6模块输出的值是引用
CommonJS
CommonJS是一个值的拷贝也就是说,一旦输出一个值,模块内部的变化就影响不到这个值
例子 来自
// lib.js
var counter = 3;
function incCounter() {
counter++;
}
module.exports = {
counter: counter,
incCounter: incCounter,
};
上面代码输出内部变量counter和改写这个变量的内部方法incCounter。然后,在main.js里面加载这个模块。
// main.js
var mod = require('./lib');
console.log(mod.counter); // 3
mod.incCounter();
console.log(mod.counter); // 3
上面代码说明,lib.js模块加载以后,它的内部变化就影响不到输出的mod.counter了。这是因为mod.counter是一个原始类型的值,会被缓存。除非写成一个函数,才能得到内部变动后的值。
// lib.js
var counter = 3;
function incCounter() {
counter++;
}
module.exports = {
// 取值器函数
get counter() {
return counter
},
incCounter: incCounter,
};
ES6 模块 (module)
ES6 模块是引用,并且不会缓存值,模块里面的变量绑定其所在的模块。
// m1.js
export var foo = 'bar';
setTimeout(() => foo = 'baz', 500);
// m2.js
import {foo} from './m1.js';
console.log(foo);
setTimeout(() => console.log(foo), 500);
// 输出
bar
baz
注意
export 后面不可以跟一个引用变量(export命令用于规定模块的对外接口)所以后面是声明(var function 这样的关键字)或者{ }
inport 和 export 是在一个独立的模块依赖的解析阶段
// 报错
export 1;
// 报错
var m = 1;
export m;
// 报错
function f() {}
export f;
// 写法一
export var m = 1;
// 写法二
var m = 1;
export {m};
// 写法三
var n = 1;
export {n as m};
// 正确
export function f() {};
// 正确
function f() {}
export {f};
export default
因为export default命令其实只是输出一个叫做default的变量,所以它后面不能跟变量声明语句。
// 正确
export var a = 1;
// 正确
var a = 1;
export default a;
// 错误
export default var a = 1;
import
由于import是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构
// 报错
import { 'f' + 'oo' } from 'my_module';
// 报错
let module = 'my_module';
import { foo } from module;
// 报错
if (x === 1) {
import { foo } from 'module1';
} else {
import { foo } from 'module2';
}
CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
第二个差异是因为 CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段(提升)就会生成。
CommonJS是require()同步加载模块,ES6模块是import命令是异步加载,有一个独立的模块依赖的解析阶段
最后,export命令可以出现在模块的任何位置,只要处于模块顶层就可以。如果处于块级作用域内,就会报错,下一节的import命令也是如此
注意
commonJS 中 module.exports 和 exports区别
module.exports 和 export 之间有什么区别?
前者公开了它指向的对象。 后者公开了它指向的对象的属性。
其实在 Node 的源码中,有几行代码能很好的解释他们的关系:
// 1. 用 = 赋值,使 exports 成为 module.exports 的一份副本;默认是空对象
// 其中`this`就是我们的 module 实例
const exports = this.exports; // 等价于 const exports = module.exports;
// 2. require函数的返回值如下(这里有做逻辑上的简化处理)
const require = function(id) {
// ...
return module.exports;
};
正常使用
// moduleA.js
module.exports.name = "I am moduleA";
exports.age = 20;
// index.js
const moduleA = require("./moduleA.js");
console.log("moduleA: ", moduleA); // moduleA: { name: 'I am moduleA', age: 20 }
非正常使用
// moduleA.js
exports.age = 20;
// 重新赋值了一个新的对象
module.exports = {
score: 100,
};
// index.js
const moduleA = require("./moduleA.js");
console.log("moduleA: ", moduleA); // moduleA: { score: 100 }
注意
- import 后面的{} 其实并不是解构赋值
- import 和 export 不可以在块作用域中声明
- 静态执行和异步加载不冲突
- import() 在webpack是可以按需加载的