我在Node.js模块中找到了以下合同:
module.exports = exports = nano = function database_module(cfg) {...}
我想知道module.exports
和exports
之间的区别是什么,为什么两者都在这里使用。
答案 0 :(得分:430)
即使问题早已得到回答和接受,我只想分享我的2美分:
你可以想象,在你的文件的最开始,有一些东西(仅用于解释):
var module = new Module(...);
var exports = module.exports;
因此,无论您做什么,请记住,当您从其他地方需要该模块时,将从您的模块返回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
或exports
的内容;(再次请记住,当您需要某些内容时,将返回module.exports,而不是导出)
module.exports = function Something() {
console.log('bla bla');
}
现在返回的结果类型为'function'
,您可以要求它并立即调用,如:
var x = require('./file1.js')();
因为您将返回的结果覆盖为函数。
但是,使用exports
您无法使用以下内容:
exports = function Something() {
console.log('bla bla');
}
var x = require('./file1.js')(); //Error: require is not a function
因为使用exports
,引用不再“指向”module.exports
指向的对象,因此exports
和module.exports
之间不再存在关联。在这种情况下,module.exports仍然指向将返回的空对象{}
。
来自其他主题的接受答案也应该有所帮助: Does Javascript pass by reference?
答案 1 :(得分:395)
设置module.exports
允许database_module
函数在required
时像函数一样被调用。简单地设置exports
将不允许该功能
导出,因为节点导出对象module.exports
引用。以下代码不允许用户调用该函数。
以下操作无效。
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
最初引用的属性。虽然 Node.js 确实导出了对象module.exports
引用,但允许您像函数一样调用它。
他们同时设置了module.exports
和exports
,以确保exports
不引用先前导出的对象。通过设置两者,您可以使用exports
作为速记,并在以后避免潜在的错误。
使用exports.prop = true
代替module.exports.prop = true
可以保存字符并避免混淆。
答案 2 :(得分:200)
基本上答案在于通过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 = [];
}
如您所见,每个模块对象都有一个名为exports
的属性。这是最终作为require
的一部分返回的内容。
require的下一步是将file1.js的内容包装成一个匿名函数,如下所示:
(function (exports, require, module, __filename, __dirname) {
//contents from file1.js
module.exports = '123;
});
这个匿名函数以下面的方式调用,module
这里指的是之前创建的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
仍将指向作为模块实例的一部分创建的对象。
exports = {};
因此,向上述导出对象添加任何内容都不会对module.exports对象产生任何影响,并且任何内容都不会作为require的一部分导出或返回。
答案 3 :(得分:75)
最初,module.exports=exports
和require
函数返回module.exports
引用的对象。
如果我们向对象添加属性,比如说exports.a=1
,那么module.exports和exports 仍然引用同一个对象。因此,如果我们调用require并将模块分配给变量,则变量具有属性a且其值为1;
但是,如果我们覆盖其中一个,例如exports=function(){}
,那么它们现在不同:exports指的是一个新对象和module.exports参考原始对象。如果我们需要该文件,它将不会返回新对象,因为module.exports不引用新对象。
对我来说,我将继续添加新属性,或将它们都覆盖到新对象。只是覆盖一个是不对的。请记住module.exports
是真正的老板。
答案 4 :(得分:46)
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() { ... }
答案 5 :(得分:24)
与JavaScript中引用传递对象的方式有细微差别。
exports
和module.exports
都指向同一个对象。 exports
是变量,module.exports
是模块对象的属性。
说我写这样的东西:
exports = {a:1};
module.exports = {b:12};
exports
和module.exports
现在指向不同的对象。修改导出不再修改module.exports。
当导入功能检查module.exports
时,它会{b:12}
答案 6 :(得分:11)
我只是进行了一些测试,结果发现,在nodejs的模块代码中,它应该是这样的:
var module.exports = {};
var exports = module.exports;
这样:
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.
exports.abc = function(){}; // works!
exports.efg = function(){}; // works!
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)
答案 7 :(得分:9)
以下是 manning 出版物中 node.js in action 一书中关于节点模块的精彩描述。
最终在您的应用程序中导出的是 module.exports。
exports 已设置
简单地作为 module.exports 的全局引用,最初被定义为
您可以添加属性的空对象。所以 exports.myFunc 只是简写
for module.exports.myFunc 。
因此,如果将 exports 设置为其他任何内容,则会破坏引用
module.exports 和 exports 。因为 module.exports 才是真正得到的
export, exports 将不再按预期工作 - 它不会引用模块
.exports 了。如果要维护该链接,可以创建 module.exports
再次参考出口:
ExecutorService es = ...;
Future t1work = new Future(...);
Future t2work = new Future(...);
Future t2update = new Future(...);
es.submit(t1work);
es.submit(t2work);
while (1) {
if (t1work.isDone() && t2work.isDone()) es.submit(t2update);
if (t1work.isDone() && t2update.isDone()) es.submit(t1work);
if (t2work.isDone() && t2update.isDone()) es.submit(t2work);
Thread.sleep(100);
}
答案 8 :(得分:6)
我经历了一些测试,我认为这可能会对这个问题有所了解......
<强> 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(){};
我们得到输出“@routes {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]}}”
所以我建议在模块定义中始终使用module.exports
。
我并不完全理解Node内部发生了什么,但如果您能更好地理解这一点,请发表评论,因为我确信它会有所帮助。
- 快乐编码
答案 9 :(得分:3)
我发现这个链接对回答上述问题很有用。
http://timnew.me/blog/2012/04/20/exports-vs-module-exports-in-node-js/
添加到其他帖子节点中的模块系统
var exports = module.exports
执行代码之前。所以当你想要export = foo时,你可能想做module.exports = exports = foo但是使用exports.foo = foo应该没问题
答案 10 :(得分:3)
来自docs
exports变量在模块的文件级范围内可用,并在评估模块之前分配module.exports的值。
它允许使用快捷方式,因此module.exports.f = ...可以更简洁地编写为exports.f = .... 但是,要注意像任何变量一样,如果为导出分配了一个新值,它不再绑定到module.exports:
它只是一个指向module.exports的变量。
答案 11 :(得分:3)
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将为空Object {}
var a ={},md={};
exp =a;
md.exp =a;
exp = function(){ console.log('Do nothing...'); };
console.log(md.exp); //{}
答案 12 :(得分:3)
这是
的结果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规范仅允许使用exports变量来公开公共成员。因此,命名导出模式是唯一真正与CommonJS规范兼容的模式。 module.exports的使用是Node.js提供的扩展,以支持更广泛的模块定义模式。
答案 13 :(得分:3)
这显示require()
如何以最简单的形式运作,摘自Eloquent JavaScript
<强>问题强>
模块不可能直接导出除导出对象之外的值,例如函数。例如,模块可能只想导出它定义的对象类型的构造函数。现在,它无法做到这一点,因为要求始终使用它创建的exports
对象作为导出值。
<强>解决方案强>
为模块提供另一个变量module
,该变量是具有属性exports
的对象。此属性最初指向由require创建的空对象,但可以用其他值覆盖以导出其他值。
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);
答案 14 :(得分:2)
“如果您希望模块导出的根目录是函数(例如构造函数),或者如果要在一个赋值中导出完整对象而不是一次构建一个属性,请将其分配给模块。出口而不是出口。“ - http://nodejs.org/api/modules.html
答案 15 :(得分:2)
要了解这些差异,您必须首先了解Node.js在运行时对每个模块的作用。 Node.js为每个模块创建一个包装函数:
(function(exports, require, module, __filename, __dirname) {
})()
请注意,第一个参数exports
是一个空对象,第三个参数module
是一个具有许多属性的对象,其中一个属性名为exports
。这就是exports
的来源和module.exports
的来源。前一个是变量对象,后一个是module
对象的属性。
在模块中,Node.js在开始时会自动执行以下操作:module.exports = exports
,并且最终返回module.exports
。
因此,您可以看到,如果您为exports
重新分配了一些值,则对module.exports
不会有任何影响。 (仅是因为exports
指向另一个新对象,但是module.exports
仍然保留了旧的exports
)
let exports = {};
const module = {};
module.exports = exports;
exports = { a: 1 }
console.log(module.exports) // {}
但是,如果您更新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
,则对于exports
更新似乎没有意义。 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')}
答案 16 :(得分:1)
1.exports - &gt;用作单身实用程序
2. module-exports - &gt;用作服务,模型等逻辑对象
答案 17 :(得分:1)
让我们用两种方式创建一个模块:
单程
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;
}
答案 18 :(得分:1)
您创建的每个文件都是一个模块。模块是一个对象。它具有名为exports : {}
的属性,默认情况下为空对象。
您可以创建函数/中间件,并将其添加到此空的导出对象中,例如exports.findById() => { ... }
,然后将require
添加到应用程序中的任何位置并使用...
controllers / user.js
exports.findById = () => {
// do something
}
在 routes.js 中要求使用:
const {findyId} = './controllers/user'
答案 19 :(得分:0)
module.js文件用于运行module.load系统。每当节点执行一个文件时,它包装你的js文件内容如下
'(function (exports, require, module, __filename, __dirname) {',+
//your js file content
'\n});'
因为这个包装在ur js源代码中你可以访问export,require,module等。 使用这种方法是因为没有其他方法可以将js文件中的功能写入另一个。
然后节点使用c ++执行这个包装函数。在那一刻,传入此函数的导出对象将被填充。
你可以在里面看到这个函数参数export和module。 实际上,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();
将此代码复制到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
foo的object.keys: name是function(){console.log('function to module exports')} 功能到模块导出
现在删除a.js中的注释行并注释该行上方的行 并删除b.js的最后一行并运行。
在javascript世界中,您无法重新分配作为参数传递的对象,但是当该函数的对象设置为另一个函数的参数时,您可以更改函数的公共成员
使用module.exports on且仅当您在使用require关键字时想要获取函数时才使用。 在上面的例子中我们var foo = require(a.js);你可以看到我们可以将foo称为函数;
这是节点文档解释它的方式 “导出对象是由Module系统创建的。有时这是不可接受的,许多人希望他们的模块成为某个类的实例。为此,请将所需的导出对象分配给module.exports。”
答案 20 :(得分:0)
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
。
所以,如果exports
和module.exports
指向相同的事情,你会很高兴。
答案 21 :(得分:0)
为什么两者都在这里使用
我相信他们只是想明确module.exports
,exports
和nano
指向同一个函数 - 允许您使用任一变量来调用文件中的函数。 nano
为函数提供了一些上下文。
exports
将不会被导出(只有module.exports
会),所以为什么还要覆盖那个呢?
详细程度权衡限制了未来错误的风险,例如在文件中使用exports
而不是module.exports
。它还提供澄清,module.exports
和exports
实际上指向相同的值。
module.exports
vs exports
只要您不重新分配module.exports
或exports
(而是将值添加到他们所引用的对象),您就不会有任何问题并且可以安全地使用{{1更简洁。
当分配给非对象时,它们现在指向可能令人困惑的不同位置,除非您故意希望exports
是特定的(例如函数)。
将module.exports
设置为非对象没有多大意义,因为您必须在最后设置exports
才能在其他文件中使用它。
module.exports = 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
分配给函数?更简洁!比较第二个例子的缩短程度:
helloWorld1.js:module.exports
app1.js:module.exports.hello = () => console.log('hello world');
let sayHello = require('./helloWorld1'); sayHello.hello; // hello world
app2.js:module.exports = () => console.log('hello world');
答案 22 :(得分:0)
module.exports
和exports
都在评估模块之前都指向同一对象。
当您的模块在另一个使用module.exports
语句的模块中使用时,添加到require
对象的任何属性都将可用。 exports
是可用于同一事物的快捷方式。例如:
module.exports.add = (a, b) => a+b
等同于写作:
exports.add = (a, b) => a+b
因此,只要您不为exports
变量分配新值,就可以。当您执行以下操作时:
exports = (a, b) => a+b
在为exports
分配新值时,它不再引用导出的对象,因此将保留在模块本地。
如果您打算为module.exports
分配一个新值,而不是向可用的初始对象添加新属性,则可能应该考虑如下操作:
module.exports = exports = (a, b) => a+b