Javascript Module ES6 vs CommonJS

CommonJS 模块输出的是一个值的拷贝

CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。

// lib.js
var counter = 3;
function incCounter() {
  counter++;
}
 
module.exports = {
  counter: counter,
  incCounter: incCounter,
};
// main.js
var mod = require('./lib');
 
console.log(mod.counter);
mod.incCounter();
console.log(mod.counter);
 

lib.js 模块加载以后,它的内部变化就影响不到输出的 mod.counter 了。这是因为mod.counter是一个原始类型的值,会被缓存。

$ node main.js
3
3
那麼要如何才能得到内部变动后的值?

可以写成一个函数

// lib.js
var counter = 3;
function incCounter() {
  counter++;
}
module.exports = {
  get counter() {
	return counter
  },
  incCounter: incCounter,
};
 

上面代码中,输出的 counter 属性实际上是一个取值器函数。现在再执行 main.js,就可以正确读取内部变量 counter 的变动了。

$ node main.js
3
4

ES6 模块输出的是值的引用

ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。换句话说,ES6 的 import 有点像 Unix 系统的“符号连接”,原始值变了,import加载的值也会跟着变。因此,ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。

// lib.js
export let counter = 3;
export function incCounter() {
counter++;
}
// main.js
import { counter, incCounter } from './lib';
console.log(counter); 
incCounter();
console.log(counter);

上面代码说明,ES6 模块输入的变量counter是活的,完全反应其所在模块lib.js内部的变化。

$ node main.js
3
4

Reference

Module 的加载实现 - ES6 教程 - 网道