module 对象
在每个模块中, module
的自由变量是对表示当前模块的对象的引用。 为方便起见,还可以通过全局模块的 exports
访问 module.exports
。 module
实际上不是全局的,而是每个模块本地的。
module.exports
module.exports
对象由 Module
系统创建。 有时这是不可接受的;许多人希望他们的模块成为某个类的实例。 为此,需要将期望导出的对象赋值给 module.exports
。 将期望的对象赋值给 exports
会简单地重新绑定本地的 exports
变量,这可能不是所期望的。
例如,假设正在创建一个名为 a.js
的模块:
const EventEmitter = require('events');
module.exports = new EventEmitter();
// 处理一些工作,并在一段时间后从模块自身触发 'ready' 事件。
setTimeout(() => {
module.exports.emit('ready');
}, 1000);
然后,在另一个文件中可以这么做:
const a = require('./a');
a.on('ready', () => {
console.log('模块 a 已准备好');
});
对 module.exports
的赋值必须立即完成。 不能在任何回调中完成。 以下是不起作用的:
x.js
:
setTimeout(() => {
module.exports = { a: 'hello' };
}, 0);
y.js
:
const x = require('./x');
console.log(x.a);
exports 快捷方式
exports
变量是在模块的文件级作用域内可用的,且在模块执行之前赋值给 module.exports
。
它允许使用快捷方式,因此 module.exports.f = ...
可以更简洁地写成 exports.f = ...
。 但是,就像任何变量一样,如果为 exports
赋予了新值,则它将不再绑定到 module.exports
:
module.exports.hello = true; // 从模块的引用中导出。
exports = { hello: false }; // 不导出,仅在模块中可用。
当 module.exports
属性被新对象完全替换时,通常也会重新赋值 exports
:
module.exports = exports = function Constructor() {
// ...
};
为了说明这种行为,想象对 require()
的假设实现,它与 require()
的实际实现非常类似:
function require(/* ... */) {
const module = { exports: {} };
((module, exports) => {
// 模块代码在这。在这个例子中,定义了一个函数。
function someFunc() {}
exports = someFunc;
// 此时,exports 不再是一个 module.exports 的快捷方式,
// 且这个模块依然导出一个空的默认对象。
module.exports = someFunc;
// 此时,该模块导出 someFunc,而不是默认对象。
})(module, module.exports);
return module.exports;
}