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

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

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


当前回答

module.exports和exports都指向同一个函数database_module(cfg){…}。1|变量a,b;2|a=b=function(){console.log(“旧”);};3|b=函数(){console.log(“New”);};4|5|a();//“旧”6|b();//“新建”您可以将第3行的b更改为a,输出是相反的。结论是:a和b是独立的。因此module.exports=exports=nano=function database_module(cfg){…}相当于:var f=函数database_module(cfg){…};模块导出=f;出口=f;假设上面是foo.js所需要的module.js。module.exports=exports=nano=function database_module(cfg){…}的好处现在很清楚了:在foo.js中,由于module.exports是必需的('./module.js'):var output=require('./modules.js')();在moduls.js中:可以使用exports代替module.exports。

因此,如果exports和module.exports都指向同一个对象,您会很高兴。

其他回答

我只是做了一些测试,结果发现,在nodejs的模块代码中,应该是这样的:

var module.exports = {};
var exports = module.exports;

so:

1:

exports = function(){}; // this will not work! as it make the exports to some other pointer
module.exports = function(){}; // it works! cause finally nodejs make the module.exports to export.

2:

exports.abc = function(){}; // works!
exports.efg = function(){}; // works!

3:但是,在这种情况下

module.exports = function(){}; // from now on we have to using module.exports to attach more stuff to exports.
module.exports.a = 'value a'; // works
exports.b = 'value b'; // the b will nerver be seen cause of the first line of code we have do it before (or later)

这是曼宁出版的动作书中关于node.js中节点模块的一个很好的描述。最终在应用程序中导出的是module.exports.exportsup简单地作为module.exports的全局引用,它最初定义为可以向其中添加财产的空对象。所以exports.myFunc只是一种速记对于module.exports.myFunc。因此,如果将导出设置为其他值,则会断开引用模块导出和导出。因为module.exports是真正的导出,导出将不再按预期工作,它不引用模块.不再导出。如果要维护该链接,可以创建module.exports再次引用导出如下:

module.exports = exports = db;

要了解这些差异,您必须首先了解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而不是导出。”-http://nodejs.org/api/modules.html

基本上,答案在于当通过require语句需要模块时会发生什么。假设这是第一次需要模块。

例如:

var x = require('file1.js');

file1.js的内容:

module.exports = '123';

执行上述语句时,将创建Module对象。其构造函数函数为:

function Module(id, parent) {
    this.id = id;
    this.exports = {};
    this.parent = parent;
    if (parent && parent.children) {
        parent.children.push(this);
    }

    this.filename = null;
    this.loaded = false;
    this.children = [];
}

正如您所看到的,每个模块对象都有一个带有名称导出的属性。这是最终作为需求的一部分返回的内容。

require的下一步是将file1.js的内容包装成一个匿名函数,如下所示:

(function (exports, require, module, __filename, __dirname) { 
    //contents from file1.js
    module.exports = '123;
});

这个匿名函数的调用方式如下,这里的模块引用前面创建的模块对象。

(function (exports, require, module, __filename, __dirname) { 
    //contents from file1.js
    module.exports = '123;
}) (module.exports,require, module, "path_to_file1.js","directory of the file1.js");

正如我们在函数内部看到的那样,exports的形式参数指的是module.exports。本质上,这是为模块程序员提供的便利。

然而,这种便利需要谨慎使用。在任何情况下,如果尝试将新对象分配给导出,请确保这样做。

exports = module.exports = {};

如果我们按照错误的方式执行,module.exports将仍然指向作为模块实例一部分创建的对象。

exports = {};

因此,向上面的导出对象添加任何内容都不会对module.exports对象产生任何影响,也不会作为require的一部分导出或返回任何内容。