你的直觉是正确的。我将从底层开始工作:
Node.js包装器
在运行任何文件之前,Node.js会在一个
立即调用函数表达式(IIFE)中
包装整个脚本:
(function (exports, require, module, __filename, __dirname) {
});
这是如何将“module”和“exports”变量引入作用域的方法;它们只是函数参数,并没有什么特别之处。
使用“exports”
Node.js文档中关于“module.exports”和“exports”别名的介绍非常有帮助。首先,“module.exports”和“exports”变量都指向由模块系统创建的相同空对象。
如果一个模块只需要导出一个简单的JavaScript对象并设置一些属性,“exports”就足够了。
exports.get = function(key) {
};
exports.set = function(key, value) {
};
当代码使用
require()
导入此模块时,结果将类似于:
{
get: [Function],
set: [Function]
}
替换module.exports
然而,Express将构造函数createApplication
作为根值导出。要导出函数本身,而不仅仅是将其分配给导出对象的属性,必须首先完全替换导出的对象:
module.exports = createApplication
现在,
exports
没有更新并且仍然指向旧对象,而
module.exports
是对
createApplication
的引用。但是
如果您继续阅读, 您会注意到Express除构造函数之外还导出了其他几个属性。虽然将这些分配给
module.exports
的新值的属性足以,但重新分配
exports
使其再次成为
module.exports
的别名,然后将这些属性分配给
exports
,这相当于将它们分配给
module.exports
。
因此,这些示例在功能上是等效的:
module.exports = createApplication;
function createApplication() {
}
module.exports.application = proto;
module.exports.request = req;
module.exports.response = res;
但这个更简洁:
exports = module.exports = createApplication;
function createApplication() {
}
exports.application = proto;
exports.request = req;
exports.response = res;
无论哪种方式,结果都是一样的,在根目录下有一个名为
createApplication
的函数,其属性可供使用:
{
[Function: createApplication]
application: [Object],
request: [Object],
response: [Object],
}
function(module, exports, /* two other */) { /* the actual module code */ }
。我怀疑exports
只是module.exports
的简写,因为我遇到了这两种赋值方式。所以,我认为使用哪个左侧并没有区别,而且exports = module.exports
是多余的。 - try-catch-finally