CommonJS规范的模块加载机制与ES6的有所不同,ES6属于静态加载,直到加载模块的脚本运行才会运行要加载的模块。而CommonJS的模块是动态加载,要加载的模块先运行,然后输出一个exports对象给加载模块的脚本,exports对象的属性值已经被求值。加载模块的脚本会声明一个对象来将exports对象(浅)拷贝过来。
看如下的两份不同的代码:
module_a.js
var count = 3;
var increase = funcition () {
count ++;
}
exports.count = count;
expors.increase = increase;
module_b.js
var mod = require('module_a.js');
console.log(mod.count); // 3
mod.increase(); // 这将改变module_a.js的count值;
console.log(mode.count); // 3
module_a.js的exports对象在加载后已经成了
exports = {
count: 3,
increase: increase // 这里是increase函数的引用
}
而mod从exports对象浅拷贝而来,调用increase()相当于调用module_a.js的increase函数,对mod对象并无变化;
module_a.js
var count = [1, 2, 3];
var increase = funcition () {
count[2] = 0;
}
exports.count = count;
expors.increase = increase;
module_b.js
var mod = require('module_a.js');
console.log(mod.count); // [1, 2, 3]
mod.increase(); // 这将改变module_a.js的count值,同时因为mod.count也引用了module_a.js的count值,会改变mod.count的值;
console.log(mode.count); // [1, 2, 0]
当count值为引用类型时,传递给mod.count的是引用类型值,当module_a.js的count值变化时,mod.count自然也会发生变化。
因此,在加载模块时,要注意模块exports对象的属性值是否是引用类型。如果是引用类型值,更改模块内部变量时,会导致加载模块的脚本的属性值也跟着一起改变。
导入模块时,模块运行一遍输出闭包exports,因此各个文件对于模块的更改都是在自己的作用域范围内,不会互相影响。