我在Node.js模块中找到了以下契约:

module.exports = exports = nano = function database_module(cfg) {...}

我想知道module.exports和exports之间有什么区别,为什么在这里使用它们。


当前回答

我发现这个链接对回答上述问题很有用。

http://timnew.me/blog/2012/04/20/exports-vs-module-exports-in-node-js/

添加到其他帖子节点中的模块系统

var exports = module.exports 

在执行代码之前。因此,当您想要exports=foo时,您可能需要执行module.exports=exports=foo,但使用exports.foo=foo应该可以

其他回答

尽管这个问题早就得到了回答和接受,但我只想分享我的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是否通过引用传递?

“如果您希望模块导出的根是一个函数(例如构造函数),或者如果您希望在一个赋值中导出一个完整的对象,而不是一次生成一个属性,请将其分配给module.exports而不是导出。”-http://nodejs.org/api/modules.html

要了解这些差异,您必须首先了解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')}

为什么这两个都用在这里

我相信他们只是想清楚module.exports、exports和nano指向同一个函数——允许您使用任意一个变量来调用文件中的函数。nano为函数的功能提供了一些上下文。

导出不会被导出(只有module.exports会被导出),那么为什么还要麻烦覆盖它呢?

冗长的权衡限制了未来错误的风险,例如在文件中使用导出而不是module.exports。它还澄清了module.exports和exports实际上指向相同的值。


module.exports与导出

只要不重新分配module.exports或exports(而是向它们都引用的对象添加值),就不会有任何问题,并且可以安全地使用exports以更简洁。

当将其中一个分配给非对象时,它们现在指向不同的位置,这可能会令人困惑,除非您有意希望module.exports是特定的(例如函数)。

将导出设置为非对象没有多大意义,因为必须在末尾设置module.exports=导出,才能在其他文件中使用它。

let module = { exports: {} };
let exports = module.exports;

exports.msg = 'hi';
console.log(module.exports === exports); // true

exports = 'yo';
console.log(module.exports === exports); // false

exports = module.exports;
console.log(module.exports === exports); // true

module.exports = 'hello';
console.log(module.exports === exports); // false

module.exports = exports;
console.log(module.exports === exports); // true

为什么将module.exports分配给函数?

更简洁!比较第二个示例的长度:

helloWorld1.js: module.exports.hello = () => console.log('hello world');

app1.js:let sayHello=require('./helloWorld1');sayHellohello;//你好,世界

helloWorld2.js: module.exports = () => console.log('hello world');

app2.js:let sayHello=require('./helloWorld2');说你好;//你好,世界

最初,module.exports=导出,require函数返回module.exports引用的对象。

如果我们向对象添加属性,比如exports.a=1,那么module.exports和exports仍然引用同一个对象。因此,如果我们调用require并将模块分配给变量,那么该变量具有属性a,其值为1;

但是如果我们重写其中一个,例如exports=function(){},那么它们现在就不同了:exports引用新对象,module.exports引用原始对象。如果我们需要该文件,它将不会返回新对象,因为module.exports不引用新对象。

对我来说,我将继续添加新属性,或者将它们都覆盖到新对象中。只是忽略一个是不正确的。请记住,module.exports才是真正的老板。