我在 Node.js 模块中找到了以下合同:
module.exports = exports = nano = function database_module(cfg) {...}
我想知道两者之间有什么区别module.exports
以及exports
为什么在这里使用两者。
我在 Node.js 模块中找到了以下合同:
module.exports = exports = nano = function database_module(cfg) {...}
我想知道两者之间有什么区别module.exports
以及exports
为什么在这里使用两者。
尽管很久以前就已经回答并接受了问题,但我只想分享我的 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");
}
您正在添加 2 个函数a
和b
指向的对象module.exports
,因此typeof
返回结果将是object
:{ a: [Function], b: [Function] }
当然,如果您module.exports
在本例中使用而不是exports
.
在这种情况下,您希望您module.exports
的行为类似于导出值的容器。然而,如果您只想导出构造函数,那么您应该了解使用module.exports
or exports
;(再次记住,module.exports
当您需要某些东西时将返回它,而不是export
)。
module.exports = function Something() {
console.log('bla bla');
}
现在typeof
返回结果是'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
仍然指向{}
将返回的空对象。
另一个主题的公认答案也应该有所帮助: JavaScript 是否通过引用传递?
设置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
保存字符并避免混淆。
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 = [];
}
如您所见,每个模块对象都有一个名为 name 的属性exports
。这是最终作为require
.
require 的下一步是将 file1.js 的内容包装到一个匿名函数中,如下所示:
(function (exports, require, module, __filename, __dirname) {
//contents from file1.js
module.exports = '123;
});
而这个匿名函数的调用方式如下,module
这里指的Module
是前面创建的Object。
(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 的一部分导出或返回任何内容。
最初,module.exports=exports
和require
函数返回对象module.exports
所指的对象。
如果我们向对象添加属性,比如exports.a=1
,那么 module.exports 和 exports仍然引用同一个对象。所以如果我们调用require并将模块赋值给一个变量,那么这个变量有一个属性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() { ... }
如果您只是将您的 to 分配function() { ... }
给exports
,您将重新分配exports
to 不再指向module.exports
。
如果你不想module.exports
每次都引用你的函数,你可以这样做:
module.exports = exports = function() { ... }
请注意,这module.exports
是最左边的参数。
附加属性exports
是不一样的,因为您没有重新分配它。这就是为什么这有效
exports.foo = function() { ... }
这与 JavaScript 中对象通过引用传递的方式存在细微差别。
exports
并且module.exports
都指向同一个对象。exports
是一个变量,module.exports
是模块对象的一个属性。
假设我写了这样的东西:
exports = {a:1};
module.exports = {b:12};
exports
现在module.exports
指向不同的对象。修改导出不再修改 module.exports。
当导入功能检查module.exports
它得到{b:12}
我只是做了一些测试,结果发现,在 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)
这是Manning出版物中关于node.js 中节点模块的一个很好的描述。
最终在您的应用程序中导出的是module.exports。export 被简单地设置为对 module.exports的全局引用,它最初被定义为一个可以添加属性的空对象。所以exports.myFunc只是module.exports.myFunc的简写。
因此,如果export设置为其他任何值,它会破坏module.exports和exports之间
的引用。因为module.exports
是真正被导出的东西,导出将不再按预期工作——它不再引用模块 .exports。如果要维护该链接,可以再次使module.exports
引用导出,如下所示:
module.exports = exports = db;
要了解这些差异,您必须首先了解 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')}
我经历了一些测试,我认为这可能会对这个主题有所启发......
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 内部发生了什么,但如果你能更清楚地理解这一点,请发表评论,因为我相信它会有所帮助。
-- 快乐编码
我发现此链接对于回答上述问题很有用。
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 应该没问题
这显示了如何require()
以最简单的形式工作,摘自Eloquent JavaScript
问题
模块不能直接导出导出对象以外的值,例如函数。例如,一个模块可能只想导出它定义的对象类型的构造函数。现在,它不能这样做,因为 require 总是使用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);
这是结果
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 提供的扩展,用于支持更广泛的模块定义模式。
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); //{}
从文档
export 变量在模块的文件级范围内可用,并在评估模块之前分配了 module.exports 的值。
它允许使用快捷方式,因此 module.exports.f = ... 可以更简洁地写为 export.f = ...。但是,请注意,与任何变量一样,如果为 export 分配一个新值,它是不再绑定到 module.exports:
它只是一个指向 module.exports 的变量。
“如果您希望模块导出的根是一个函数(例如构造函数),或者如果您想在一个分配中导出一个完整的对象而不是一次构建一个属性,请将其分配给 module.exports 而不是出口。” - http://nodejs.org/api/modules.html
让我们用 2 种方式创建一个模块:
单程
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;
}
module.exports
并且exports
在评估模块之前都指向同一个对象。
当您的模块在另一个模块 using语句中使用时,您添加到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
1.exports -> 用作单例实用程序
2. module-exports -> 用作逻辑对象,例如服务、模型等
为什么两者都在这里使用
我相信他们只是想清楚module.exports
,exports
和nano
指向同一个函数 - 允许您使用任一变量来调用文件中的函数。nano
为函数的作用提供了一些上下文。
exports
不会被导出(只会module.exports
),那么为什么还要覆盖它呢?
冗长权衡限制了未来出现错误的风险,例如在文件中使用exports
而不是。module.exports
它还提供了澄清,module.exports
并且exports
实际上指向相同的值。
module.exports
对比exports
只要您不重新分配module.exports
或exports
(而是向它们都引用的对象添加值),您就不会有任何问题,并且可以安全地使用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
module.exports
给函数?更简洁!比较第二个例子有多短:
helloWorld1.js:module.exports.hello = () => console.log('hello world');
app1.js:let sayHello = require('./helloWorld1'); sayHello.hello; // hello world
module.exports = () => console.log('hello world');
app2.js:let sayHello = require('./helloWorld2'); sayHello; // hello world
在 node js 中,module.js 文件用于运行 module.load 系统。每次 node 执行文件时,它都会包装你的 js 文件内容,如下所示
'(function (exports, require, module, __filename, __dirname) {',+
//your js file content
'\n});'
由于这种包装在您的 js 源代码中,您可以访问导出、要求、模块等。使用这种方法是因为没有其他方法可以将 js 文件中写入的功能写入另一个文件。
然后节点使用 c++ 执行这个包装的函数。那时,传递给此函数的导出对象将被填充。
你可以在这个函数里面看到exports和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:名称为 function (){console.log('function to module exports')} function to module exports
现在删除 a.js 中的注释行并注释该行上方的行并删除 b.js 的最后一行并运行。
在javascript世界中,您不能重新分配作为参数传递的对象,但是当该函数的对象设置为另一个函数的参数时,您可以更改函数的公共成员
仅当您想在使用 require 关键字时获取函数时才使用 module.exports 。在上面的例子中,我们 var foo = require(a.js); 你可以看到我们可以调用 foo 作为一个函数;
这就是节点文档的解释方式“导出对象是由模块系统创建的。有时这是不可接受的,许多人希望他们的模块成为某个类的实例。为此,将所需的导出对象分配给 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"
您可以将b
第 3 行更改为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
。
例子:
如果exports.a = 10 那么module.exports.a = 10
如果我们在代码中明确地重新分配导出对象,例如 exports = {} 现在它失去了对 module.exports 的引用