我在Node.js模块中找到了以下契约:
module.exports = exports = nano = function database_module(cfg) {...}
我想知道module.exports和exports之间有什么区别,为什么在这里使用它们。
我在Node.js模块中找到了以下契约:
module.exports = exports = nano = function database_module(cfg) {...}
我想知道module.exports和exports之间有什么区别,为什么在这里使用它们。
当前回答
要了解这些差异,您必须首先了解Node.js在运行时对每个模块做了什么。Node.js为每个模块创建一个包装函数:
(function(exports, require, module, __filename, __dirname) {
})()
注意,第一个param导出是一个空对象,第三个param模块是一个具有许多财产的对象,其中一个财产名为exports。这是导出的来源和module.exports的来源。前者是变量对象,后者是模块对象的属性。
在模块中,Node.js在开始时自动执行以下操作:module.exports=exports,并最终返回module.exports。
因此,您可以看到,如果您为导出重新分配一些值,那么它不会对module.exports产生任何影响
let exports = {};
const module = {};
module.exports = exports;
exports = { a: 1 }
console.log(module.exports) // {}
但是如果你更新导出的财产,它肯定会对module.exports产生影响。因为它们都指向同一个对象。
let exports = {};
const module = {};
module.exports = exports;
exports.a = 1;
module.exports.b = 2;
console.log(module.exports) // { a: 1, b: 2 }
还要注意,如果您将另一个值重新分配给module.exports,那么对于导出更新来说似乎没有意义。由于module.exports指向另一个对象,因此忽略导出上的每个更新。
let exports = {};
const module = {};
module.exports = exports;
exports.a = 1;
module.exports = {
hello: () => console.log('hello')
}
console.log(module.exports) // { hello: () => console.log('hello')}
其他回答
尽管这个问题早就得到了回答和接受,但我只想分享我的2分钱:
您可以想象,在文件的开头有这样的内容(仅供解释):
var module = new Module(...);
var exports = module.exports;
因此,无论您做什么,只要记住,当您从其他地方需要模块时,模块将返回module.exports和NOT导出。
所以,当你做如下事情时:
exports.a = function() {
console.log("a");
}
exports.b = function() {
console.log("b");
}
您正在向module.exports指向的对象添加2个函数a和b,因此返回结果的类型将是一个对象:{a:[Function],b:[Function〕}
当然,如果在本例中使用module.exports而不是exports,则会得到相同的结果。
在这种情况下,您希望module.exports的行为类似于导出值的容器。然而,如果您只想导出构造函数,那么对于使用module.exports或exports,您应该知道一些事情;(请再次记住,当您需要某些东西时,会返回module.exports,而不是export)。
module.exports = function Something() {
console.log('bla bla');
}
现在返回结果的类型是“function”,您可以要求它并立即调用,如:var x=require('./file1.js')();因为您将返回结果覆盖为函数。
但是,使用导出时不能使用以下内容:
exports = function Something() {
console.log('bla bla');
}
var x = require('./file1.js')(); //Error: require is not a function
因为对于导出,引用不再指向module.exports所指向的对象,因此导出和module.export之间不再存在关系。在这种情况下,module.exports仍然指向将返回的空对象{}。
另一个主题的公认答案也应有助于:JavaScript是否通过引用传递?
这是曼宁出版的动作书中关于node.js中节点模块的一个很好的描述。最终在应用程序中导出的是module.exports.exportsup简单地作为module.exports的全局引用,它最初定义为可以向其中添加财产的空对象。所以exports.myFunc只是一种速记对于module.exports.myFunc。因此,如果将导出设置为其他值,则会断开引用模块导出和导出。因为module.exports是真正的导出,导出将不再按预期工作,它不引用模块.不再导出。如果要维护该链接,可以创建module.exports再次引用导出如下:
module.exports = exports = db;
从文档中
exports变量在模块的文件级范围内可用,并在评估模块之前为其赋值module.exports。它允许快捷方式,因此module.exports.f=。。。可以更简洁地写成exports。f=。。。。但是,请注意,与任何变量一样,如果为导出分配了新值,则不再将其绑定到module.exports:
它只是一个指向module.exports的变量。
这显示了require()如何以最简单的形式工作,摘自Eloquent JavaScript
问题模块不可能直接导出导出对象以外的值,例如函数。例如,模块可能只希望导出其定义的对象类型的构造函数。现在,它无法做到这一点,因为require始终使用它创建的导出对象作为导出值。
解决方案为模块提供另一个变量module,它是一个具有属性导出的对象。此属性最初指向require创建的空对象,但可以用另一个值覆盖以导出其他对象。
function require(name) {
if (name in require.cache)
return require.cache[name];
var code = new Function("exports, module", readFile(name));
var exports = {}, module = {exports: exports};
code(exports, module);
require.cache[name] = module.exports;
return module.exports;
}
require.cache = Object.create(null);
我通过了一些测试,我认为这可能会对这个问题有所启发。。。
应用.js:
var ...
, routes = require('./routes')
...;
...
console.log('@routes', routes);
...
/routes/index.js的版本:
exports = function fn(){}; // outputs "@routes {}"
exports.fn = function fn(){}; // outputs "@routes { fn: [Function: fn] }"
module.exports = function fn(){}; // outputs "@routes function fn(){}"
module.exports.fn = function fn(){}; // outputs "@routes { fn: [Function: fn] }"
我甚至添加了新文件:
./routes/index.js:
module.exports = require('./not-index.js');
module.exports = require('./user.js');
./routes/not-index.js:
exports = function fn(){};
./routes/user.js:
exports = function user(){};
我们得到输出“@routes{}”
./routes/index.js:
module.exports.fn = require('./not-index.js');
module.exports.user = require('./user.js');
./routes/not-index.js:
exports = function fn(){};
./routes/user.js:
exports = function user(){};
我们得到输出“@routes{fn:{},user:{}}”
./routes/index.js:
module.exports.fn = require('./not-index.js');
module.exports.user = require('./user.js');
./routes/not-index.js:
exports.fn = function fn(){};
./routes/user.js:
exports.user = function user(){};
我们得到输出“@routes{user:[Function:user]}”如果我们将user.js更改为{ThisLoadedLast:[Function:ThisLoadedLast]},我们将得到输出“@routes{ThisLoadedLast:[Function:TThisLoadedlast]}”。
但如果我们修改/routes/index.js。。。
./routes/index.js:
module.exports.fn = require('./not-index.js');
module.exports.ThisLoadedLast = require('./user.js');
./routes/not-index.js:
exports.fn = function fn(){};
./routes/user.js:
exports.ThisLoadedLast = function ThisLoadedLast(){};
…我们得到“@routes{fn:{fn:[Function:fn]},ThisLoadedLast:{ThisLoadedLast:[Function:ThisLoadedLast]}}”
所以我建议在模块定义中始终使用module.exports。
我不完全理解Node内部的情况,但如果您能理解这一点,请发表评论,因为我相信这会有所帮助。
--快乐的编码