在Node.js中,module.exports vs exports
我在Node.js模块中find了以下合同:
module.exports = exports = nano = function database_module(cfg) {...}
我不知道是什么module.exports
和exports
之间的差异,为什么这两个在这里使用。
设置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.exports
和exports
以确保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.exports
和module.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
是真正的老板。
exports
和module.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中通过引用传递对象的方式是微妙的差异。
exports
和module.exports
都指向同一个对象。 exports
是一个variables, module.exports
是模块对象的一个属性。
说我写这样的东西:
exports = {a:1}; module.exports = {b:12};
exports
和module.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.exports和exports之间的引用 。 因为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。
-
module.exports
和exports
指向相同的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
,输出反向。 结论是:a
和b
是独立的。 -
所以
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.exports
是require('./module.js')
:var output = require('./modules.js')();
-
在
moduls.js
:你可以使用exports
来代替module.exports
。
-
所以,如果module.exports
和module.exports
指向相同的东西,你会很高兴。