Node.js 模块系统

CommonJS 的 require/exports、模块缓存、循环依赖。

Intermediate
Key Points`require` 是同步加载 + 执行 + 缓存;`module.exports` 才是真正的导出对象。

一个模块是什么

Node wraps every file in a function, so top-level var does not pollute the global scope:

JavaScript
(function (exports, require, module, __filename, __dirname) {
  // 你的文件内容
});
NameMeaning
exportsmodule.exports 的初始引用
require加载其他模块的函数
module当前模块对象,含 exports
__filename当前文件的绝对路径
__dirname当前文件所在目录

导出的两种写法

JavaScript
// math.js —— 挂属性
exports.add = (a, b) => a + b;
exports.sub = (a, b) => a - b;

// 或者整体替换(推荐)
module.exports = {
  add: (a, b) => a + b,
  sub: (a, b) => a - b,
};
JavaScript
const { add } = require('./math');
console.log(add(1, 2));
Output
3
exports = { add } is a no-op — it only rebinds a local variable; module.exports is untouched. Use module.exports = {...} or exports.add = ....

缓存

JavaScript
// counter.js
let n = 0;
module.exports = () => ++n;

// a.js
const c = require('./counter');
console.log(c(), c());   // 1 2

A module executes once, on first require, and the same object is returned afterwards. Module-level variables are process-wide singletons. To get a fresh instance, export a factory.

循环依赖

JavaScript
// a.js
exports.done = false;
const b = require('./b');
console.log('在 a 里,b.done =', b.done);
exports.done = true;

// b.js
exports.done = false;
const a = require('./a');
console.log('在 b 里,a.done =', a.done);   // false —— 拿到的是半成品
exports.done = true;
Output
在 b 里,a.done = false
在 a 里,b.done = true

When b requires a, a is only half-executed, so it sees whatever was exported so far (done = false). Circular dependencies are almost always a design smell — use injection or extract a third module.

Friends & Partners