在Node.js中,module.exports vs exports

我在Node.js模块中find了以下合同:

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

我不知道是什么module.exportsexports之间的差异,为什么这两个在这里使用。

设置module.exports允许database_module函数在required时被调用。 简单地设置exports将不允许导出函数,因为节点导出对象module.exports引用。 下面的代码将不允许用户调用该函数。

module.js

以下将无法正常工作。

 exports = nano = function database_module(cfg) {return;} 

如果module.exports被设置,以下将会工作。

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

安慰

 var func = require('./module.js'); // the following line will **work** with module.exports func(); 

基本上, node.js不会导出exports当前引用的对象,而是exports最初引用的导出属性。 尽pipeNode.js不会导出对象module.exports引用,但可以将其称为函数。


第二个不重要的原因

他们设置module.exportsexports以确保exports不会引用先前导出的对象。 通过设置您使用exports作为速记,并避免以后的潜在错误的道路上。

使用module.exports.prop = true exports.prop = true而不是module.exports.prop = true保存字符并避免混淆。

即使问题早已得到回答和接受,只想分享我的2分钱:

你可以想象,在你的文件的开始处有一些像(只是为了解释):

 var module = new Module(...); var exports = module.exports; 

在这里输入图像描述

所以不pipe你做什么,只要记住module.exports和NOT exports将从你的模块返回,当你需要从其他地方的模块。

所以当你做这样的事情时:

 exports.a = function() { console.log("a"); } exports.b = function() { console.log("b"); } 

你要在module.exports指向的对象上添加2个函数'a'和'b',所以typeof返回结果将是一个object{ a: [Function], b: [Function] }

当然,如果您在本例中使用module.exports而不是exports则会得到相同的结果。

在这种情况下,您希望module.exports的行为与导出值的容器类似。 在你只想导出构造函数的情况下,你会做什么? (再次记住,module.exports将返回,当你需要的东西,而不是出口)。

 module.exports = function Something() { console.log('bla bla'); } 

现在typeof返回结果是'function' ,你可以要求它,并立即调用像:
var x = require('./file1.js')(); 因为你覆盖返回的结果是一个函数。

为什么在这种情况下,你不能使用像这样的东西:

 exports = function Something() { console.log('bla bla'); } 

因为这个exports引用不再指向module.exports指向的对象,所以module.exportsmodule.exports之间没有关系了。 在这种情况下,module.exports仍然指向将被返回的空对象{}

从另一个主题接受的答案也应该帮助: 是否通过引用的JavaScript?

基本上,答案就在于通过require语句需要一个模块时真正发生的事情。 假设这是第一次需要该模块。

例如:

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

file1.js的内容:

 module.exports = '123'; 

当执行上述语句时,将创build一个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 = []; } 

正如你看到的每个模块对象都有一个名称为exports的属性。 这是最终返回作为require一部分。

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

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

这个匿名函数被调用的方式如下,这里的Module是指前面创build的Module对象。

 (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仍将指向作为模块实例的一部分创build的对象。

 exports = {}; 

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

最初, module.exports=exports ,而require函数返回对象module.exports指的是。

如果我们将属性添加到对象,例如exports.a exports.a=1 ,那么module.exports和exports 仍然引用同一个对象。 所以如果我们调用require并把模块赋值给一个variables,那么这个variables有一个属性a,它的值是1;

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

对于我来说,我会不断添加新的属性,或者将它们都覆盖到一个新的对象上。 只是重写一个是不对的。 请记住, module.exports是真正的老板。

exportsmodule.exports是相同的,除非你重新分配你的模块内的exports

想想最简单的方法就是认为这条线隐含在每个模块的顶部。

 var exports = module.exports = {}; 

如果在你的模块中,你重新分配exports ,那么你在模块中重新分配它,它不再等于module.exports 。 这就是为什么,如果你想导出一个函数,你必须这样做:

 module.exports = function() { ... } 

如果你简单地将你的function() { ... }分配给exports ,你将重新分配exports到不再指向module.exports

如果你不想每次都通过module.exports来引用你的函数,你可以这样做:

 module.exports = exports = function() { ... } 

注意module.exports是最左边的参数。

将属性附加到exports是不相同的,因为您不重新分配它。 这就是为什么这个工作

 exports.foo = function() { ... } 

JavaScript通过引用传递

在JavaScript中通过引用传递对象的方式是微妙的差异。

exportsmodule.exports都指向同一个对象。 exports是一个variables, module.exports是模块对象的一个​​属性。

说我写这样的东西:

 exports = {a:1}; module.exports = {b:12}; 

exportsmodule.exports现在指向不同的对象。 修改导出不再修改module.exports。

当导入函数检查module.exports它得到{b:12}

我只是做了一些testing,事实certificate,在nodejs的模块代码中,它应该是这样的:

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

所以:

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) 

下面是关于Manning出版物的操作手册中node.js节点模块的一个很好的描述。
在应用程序中最终导出的是module.exports。
导出只是简单地设置为对module.exports的全局引用,它最初被定义为一个可以添加属性的空对象。 所以exports.myFunc只是module.exports.myFunc的简写。

因此,如果导出设置为其他任何内容,则会打破module.exportsexports之间的引用 。 因为module.exports是真正被导出的东西, 导出将不再像预期的那样工作 – 它不再引用module .exports 。 如果你想保持这个链接,你可以再次使module.exports引用导出如下:

 module.exports = exports = db; 

我经历了一些testing,我认为这可能会揭示这个问题。

app.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(){}; 

如果我们将user.js改为{ ThisLoadedLast: [Function: ThisLoadedLast] } ,我们得到输出“@routes {ThisLoadedLast:[Function:ThisLoadedLast]} ”。


但是,如果我们修改./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]}}

所以我会build议在模块定义中总是使用module.exports

我并不完全理解Node内部发生了什么,但是请评论一下,如果您能更好地理解这一点,我相信这会有所帮助。

– 快乐的编码

这显示了require()如何以最简单的forms工作,摘自Eloquent JavaScript

问题模块不能直接导出导出对象以外的值,例如函数。 例如,一个模块可能只想导出它定义的对象types的构造函数。 现在,它不能这样做,因为require始终使用它创build的exports对象作为导出的值。

解决scheme为模块提供另一个variablesmodule ,它是一个具有属性exports的对象。 该属性最初指向由require创build的空对象,但可以用另一个值覆盖以导出其他值。

 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); 

这是结果

 console.log("module:"); console.log(module); console.log("exports:"); console.log(exports); console.log("module.exports:"); console.log(module.exports); 

在这里输入图像描述

也:

 if(module.exports === exports){ console.log("YES"); }else{ console.log("NO"); } //YES 

注意:CommonJS规范只允许使用exportsvariables来公开成员。 因此,命名的出口模式是唯一真正与CommonJS规范兼容的模式。 module.exports的使用是由Node.js提供的扩展,以支持更广泛的模块定义模式。

 var a = {},md={}; 

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

 exp = a;//exports =a; md.exp = a;//module.exports = a; exp.attr = "change"; console.log(md.exp);//{attr:"change"} 

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

 var a ={},md={}; exp =a; md.exp =a; exp = function(){ console.log('Do nothing...'); }; console.log(md.exp); //{} 

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

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

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

 var exports = module.exports 

在执行你的代码之前。 所以当你想export = foo的时候,你可能想要做module.exports = exports = foo但是使用exports.foo = foo应该没问题

“如果你希望模块导出的根是一个函数(比如构造函数),或者如果你想在一个赋值中导出一个完整的对象,而不是一次构build一个属性,那么将它赋值给module.exports,而不是出口“。 – http://nodejs.org/api/modules.html

从文档

exportsvariables在模块的文件级范围内可用,并且在评估模块之前分配module.exports的值。

它允许一个快捷方式,以便module.exports.f = …可以写得更加简洁,如exports.f = ….但是,请注意,像任何variables,如果一个新的值被分配到出口,它是不再绑定到module.exports:

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

1.exports – >用作单例实用程序
2.模块导出 – >用作服务,模型等逻辑对象

我们用两种方法创build一个模块:

单程

 var aa = { a: () => {return 'a'}, b: () => {return 'b'} } module.exports = aa; 

第二种方式

 exports.a = () => {return 'a';} exports.b = () => {return 'b';} 

这就是require()将如何集成模块。

第一种方法:

 function require(){ module.exports = {}; var exports = module.exports; var aa = { a: () => {return 'a'}, b: () => {return 'b'} } module.exports = aa; return module.exports; } 

第二种方式

 function require(){ module.exports = {}; var exports = module.exports; exports.a = () => {return 'a';} exports.b = () => {return 'b';} return module.exports; } 

在节点js module.js文件是用来运行module.load系统。每次当节点执行一个文件它包装你的js文件内容如下

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

由于这个包装在你的js源代码里面,所以你可以访问exports,require,module等等。这个方法的用处是因为没有其他方法可以把js文件写到另一个。

然后节点使用c ++执行这个包装函数。 那时候传入这个函数的输出对象就会被填充。

你可以在里面看到这个函数的参数出口和模块。 实际上出口是模块构造函数的公共成员。

看看下面的代码

将此代码复制到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(); 

将此代码复制到a.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 

导出是[object Object]

foo的object.keys:name是function(){console.log('function to module exports')}函数来模块导出

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

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

请记住

使用module.exports当且仅当你想使用require关键字时才能得到一个函数。 在上面的例子中,我们var foo = require(a.js); 你可以看到我们可以调用foo作为函数;

这就是节点文档如何解释它“导出对象是由Module系统创build的,有时这是不可接受的,很多人希望它们的模块是某个类的实例,为此将所需的导出对象分配给module.exports。

  1. module.exportsexports指向相同的function database_module(cfg) {...}

     1| var a, b; 2| a = b = function() { console.log("Old"); }; 3| b = function() { console.log("New"); }; 4| 5| a(); // "Old" 6| b(); // "New" 

    您可以将第3行上的b更改为a ,输出反向。 结论是:

    ab是独立的。

  2. 所以module.exports = exports = nano = function database_module(cfg) {...}等价于:

     var f = function database_module(cfg) {...}; module.exports = f; exports = f; 

    假设上面是module.js ,它是foo.js所要求的。 module.exports = exports = nano = function database_module(cfg) {...}的好处现在已经清楚了:

    • foo.js ,由于module.exportsrequire('./module.js')

       var output = require('./modules.js')(); 
    • moduls.js :你可以使用exports来代替module.exports

所以,如果module.exportsmodule.exports指向相同的东西,你会很高兴。