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

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

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


当前回答

在节点js中,module.js文件用于运行module.load系统。每当节点执行文件时,它都会将js文件内容包装如下

'(function (exports, require, module, __filename, __dirname) {',+
     //your js file content
 '\n});'

由于这种包装在urjs源代码中,您可以访问导出、require、模块等。。之所以使用这种方法,是因为没有其他方法可以将js文件中的功能写入另一个文件。

然后节点使用c++执行这个包装函数。此时,传递到此函数的exports对象将被填充。

您可以在这个函数中看到参数导出和模块。实际上,exports是模块构造函数函数的公共成员。

查看以下代码

将此代码复制到b.js中

console.log("module is "+Object.prototype.toString.call(module));
console.log("object.keys "+Object.keys(module));
console.log(module.exports);
console.log(exports === module.exports);
console.log("exports is "+Object.prototype.toString.call(exports));
console.log('----------------------------------------------');
var foo = require('a.js');
console.log("object.keys of foo: "+Object.keys(foo));
console.log('name is '+ foo);
foo();

将此代码复制到.js

exports.name = 'hello';
module.exports.name = 'hi';
module.exports.age = 23;
module.exports = function(){console.log('function to module exports')};
//exports = function(){console.log('function to export');}

现在使用节点运行

这是输出

module is [object Object]
object.keys id,exports,parent,filename,loaded,children,paths
{}
true

导出为[对象对象]

foo的object.keys:名称是函数(){console.log('模块导出函数')}功能到模块导出

现在删除.js中的注释行,并注释该行上方的行然后删除b.js的最后一行并运行。

在javascript世界中,不能重新分配作为参数传递的对象,但当该函数的对象设置为另一个函数的参数时,可以更改该函数的公共成员

请记住

仅当您想在使用require关键字时获取函数时,才使用module.exports。在上面的例子中,我们varfoo=require(a.js);你可以看到我们可以将foo作为函数调用;

节点文档就是这样解释的导出对象是由Module系统创建的。有时这是不可接受的,许多人希望自己的模块是某个类的实例。为此,请将所需的导出对象分配给Module.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应该可以

基本上,答案在于当通过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的一部分导出或返回任何内容。

从文档中

exports变量在模块的文件级范围内可用,并在评估模块之前为其赋值module.exports。它允许快捷方式,因此module.exports.f=。。。可以更简洁地写成exports。f=。。。。但是,请注意,与任何变量一样,如果为导出分配了新值,则不再将其绑定到module.exports:

它只是一个指向module.exports的变量。

为什么这两个都用在这里

我相信他们只是想清楚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');说你好;//你好,世界

var a = {},md={};

//首先,exports和module.exports指向同一个空Object

exp = a;//exports =a;
md.exp = a;//module.exports = a;

exp.attr = "change";

console.log(md.exp);//{attr:"change"}

//如果将exp指向其他对象,而不是将其属性指向其他对象。md.exp将为空对象{}

var a ={},md={};
exp =a;
md.exp =a;

exp = function(){ console.log('Do nothing...'); };

console.log(md.exp); //{}