我在Node.js模块中找到了以下合同: >

评论

后代:nodejs.org/docs/latest/api/modules.html#module.exports

很棒的资源:hacksparrow.com/node-js-exports-vs-module-exports.html ^ _ ^

更新了“后代”链接:nodejs.org/docs/latest/api/modules.html#modules_module_exports

这都是关于参考的。将导出想像成指向module.exports的局部变量对象。如果您夸大了export的值,那么您将丢失对module.exports的引用,而module.exports是作为公共接口公开的内容。

快速摘要:除非您重新分配一个对象,否则export和module.exports都指向同一个对象。最后,返回module.exports。因此,如果您将导出重新分配给函数,则不要期望该函数,因为它不会返回。但是,如果您已分配了类似于exports.func = function ...的函数,则结果将具有func属性,并将function作为值。因为您将属性添加到了导出指向的对象中。.

#1 楼

设置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最初引用的属性。尽管Node.js确实会导出对象module.exports引用,但允许您像调用函数一样调用它。先前导出的对象。通过同时设置两者,可以将module.exports用作简写,并避免以后出现潜在的错误。
使用exports而不是exports可以节省字符并避免混淆。

评论


@ajostergaard:它恰好是OP的示例所来自的库的名称。在该模块中,它允许作者编写诸如nano.version ='3.3'之类的内容,而不是module.exports.version ='3.3'之类的东西,后者的读法更加清晰。 (请注意,nano是局部变量,在设置模块导出之前已声明。)

– josh3736
13年1月14日在20:19

@lime-谢谢-我很高兴这基本上无关紧要,因为如果不是,那意味着我会完全误解了一切。 :-| :)

– ostergaard
13年1月14日在23:18

嗨,莱姆,这是一个很老的答案,但我希望您能澄清一下。如果我要设置module.exports但不设置export,我的代码是否仍然可以使用?谢谢你的帮助!

–阿萨德·塞德丁(Asad Saeeduddin)
13年2月21日在0:42

@Asad是,如果您设置了module.exports,该函数将正确导出

–石灰
13年2月21日在23:19

@Liam感谢您的宝贵回答。还有几个查询-在server.js条目中,预计module.exports和exports的值是什么?是module.exports应该为null并将导出设置为空对象吗?是这种传统还是有一些有效的用例来将export和module.exports指向两个不同的对象?

– Sushil
13年5月7日在6:45



#2 楼

即使很久以前已经回答并接受了问题,我也只想分享我的2美分:

您可以想象在文件的开头有类似的内容(仅供说明):

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




所以无论做什么,只要记住当您需要module.exports和not exports将从模块中返回模块是从其他位置来的。 a也指向点,因此返回的结果是b: >
就是这种情况,您希望module.exports表现得像一个导出值的容器。而如果只想导出构造函数,那么使用typeofobject时,您应该了解一些知识(再次记住,当需要某些东西而不是{ a: [Function], b: [Function] }时,将返回module.exports)。

现在exports返回的结果是module.exports,您可以要求它并立即调用,例如:
module.exports,因为您将返回结果覆盖为函数。使用exports不能使用类似以下内容的东西:

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


由于使用module.exports,引用不再指向export指向的对象,因此没有typeof'function'之间的关系不再存在。在这种情况下,var x = require('./file1.js')();仍指向将返回的空对象exports

评论


很好的解释,但我仍然不明白如何才能完全省略模块中的module.exports,例如在以下npm软件包中:github.com/tj/consolidate.js/blob/master/lib/consolidate.js

–科迪·布格斯坦(CodyBugstein)
2015年2月9日在7:46

@Imray说明在这里:JavaScript是否通过引用传递? exports.a = function(){};有效,导出= function(){}不起作用

– cirpo
2015年6月12日19:00



oooo这个答案终于解释了。基本上,导出是指可以向其添加属性的对象,但是如果将其重新分配为功能,则不再需要将属性附加到该原始对象。现在,当module.exports仍指向该对象时,导出将指向函数,因为这就是返回的内容。您可以说出口基本上已经被垃圾收集了。

–穆罕默德·乌默尔
2015年6月23日19:48

那么,使用出口有什么意义呢?如果只是可变重分配,为什么不总是使用module.exports?似乎让我感到困惑。

– jedd.ahyoung
15年7月4日在16:30

@ jedd.ahyoung写exports.something而不是module.exports.something麻烦些

–Srle
15年7月4日在17:08

#3 楼

基本上,答案在于通过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对象。 ,形式参数exports是指module.exports。从本质上讲,这是为模块程序员提供的一种便利。

但是,这种便利需要谨慎地行使。无论如何,如果要尝试为导出分配一个新对象,请确保以这种方式进行操作。

添加到作为模块实例的一部分创建的对象。

作为require的一部分导出或返回。

评论


在这里让我迷路了exports = module.exports = {};

–巨麋
2015年4月3日,凌晨3:02

我认为这应该是最好的答案,它说明了为什么func()在@William的答案中失败!

–斑鸠
15年7月17日在2:38

我看不出增加export = module.exports = app的好处。在代码的最后一行。似乎module.exports将被导出,我们将永远不会使用export,因为它再次位于代码的最后一行。那么,为什么不简单地添加module.exports = app;呢?

–lvarayut
2015年12月15日4:19



#4 楼

最初,module.exports=exportsrequire函数返回module.exports所引用的对象。如果我们向对象添加属性,例如exports.a=1,则module.exports和export仍引用同一对象。因此,如果我们调用require并将模块分配给变量,则该变量具有属性a且其值为1;

但是如果我们覆盖其中一个,例如exports=function(){},则它们为现在不同了:导出是指一个新对象,而模块是指原始对象。而且如果我们需要该文件,它将不会返回新对象,因为module.exports没有引用新对象。到一个新对象。仅仅覆盖一个是不对的。请记住,module.exports是真正的老板。

评论


是的,这实际上是真正的答案。简洁明了。其他人可能是正确的,但充满幻想的术语,而不是完全专注于该问题的答案。

–科阿
18年1月1日在2:46

这是迄今为止最明确的答案!如果您要为其添加书签,这是精确的链接:stackoverflow.com/questions/7137397/…

–lambdarookie
19年6月6日在13:58

#5 楼

除非您在模块中重新分配exports,否则module.exportsexports是相同的。 br />
var exports = module.exports = {};


如果在模块中重新分配了exports,则在模块中重新分配了它,它不再等于module.exports。因此,如果要导出函数,则必须执行以下操作:

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


如果仅将function() { ... }分配给exports,则不再将exports重新分配为指向module.exports

如果不想每次都用module.exports引用函数,则可以执行以下操作:
module.exports是最左侧的参数。

将属性附加到exports并不相同,因为您没有重新分配它。这就是为什么它起作用的原因

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


评论


这是所有答案中最容易理解的!

– Adarsh Konchady
16 Apr 26'6:50

简洁明了

– fibono
17年2月9日在20:22

了解此功能的简便方法。

– Canatto Filipe
18年2月13日在11:58

#6 楼

JavaScript通过引用的副本传递对象

与JavaScript中通过引用传递对象的方式有微妙的区别。 。 exports是变量,而module.exports是模块对象的属性。

我写这样的东西:指向不同的对象。修改导出不再修改module.exports。

当导入功能检查exports时,它将得到module.exports

评论


最好的答案恕我直言!

– AJ先生
16年6月7日在21:40

“ JavaScript通过引用传递” –否。

– xehpuk
18 Mar 15 '18 at 2:08

#7 楼

我只是做一些测试,结果发现,在nodejs的模块代码中,它应该是这样的: :

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


2:

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.


3:但是,在这种情况下, br />
exports.abc = function(){}; // works!
exports.efg = function(){}; // works!


评论


莱曼(Lyman),因此module.exports有点像该节点的“真实交易”,但是有时您需要将所有导出添加到module.exports中,除非您使用exports.namespace(上述情况2) ),在这种情况下,似乎就像Node运行了extend(module.exports,exports);将导出的所有“命名空间”添加到module.exports对象?换句话说,如果您正在使用导出,那么您可能要在其上设置属性?

–科迪
2014年3月24日22:04



#8 楼

这是曼宁(Manning)出版的操作手册中有关node.js中的节点模块的良好描述。可以将exports设置为对module.exports的全局引用,该模块最初定义为可以添加属性的
空对象。因此,exports.myFunc只是module.exports.myFunc的简写。
因此,如果将export设置为其他值,则会破坏
module.exports和export之间的引用。由于module.exports是真正导出的
,因此导出将不再按预期方式工作-它不再引用module
.exports。如果要维护该链接,可以按以下方式再次使module.exports
reference导出:

module.exports = exports = db;


#9 楼

我经过了一些测试,我认为这可能为主题提供了一些启示。

app.js

var ...
  , routes = require('./routes')
  ...;
...
console.log('@routes', routes);
...


我什至添加了新文件:

/routes/index.js./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/not-index.js

module.exports = require('./not-index.js');
module.exports = require('./user.js');


我们得到输出“ @routes {}” << />

./routes/user.js

exports = function fn(){};


./routes/index.js

exports = function user(){};


./routes/not-index.js

module.exports.fn = require('./not-index.js');
module.exports.user = require('./user.js');


我们得到输出“ @routes {fn:{},用户:{}}”


./routes/user.js

exports = function fn(){};


./routes/index.js

exports = function user(){};


./routes/not-index.js

module.exports.fn = require('./not-index.js');
module.exports.user = require('./user.js');


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


但是如果我们修改{ ThisLoadedLast: [Function: ThisLoadedLast] } ...

./routes/index.js

exports.fn = function fn(){};


./routes/index.js

exports.user = function user(){};


./routes/not-index.js

module.exports.fn = require('./not-index.js');
module.exports.ThisLoadedLast = require('./user.js');


...我们得到“ @routes {fn:{fn:[Function:fn]},ThisLoadedLast:{ThisLoadedLast:[Function:ThisLoadedLast]}}}”

所以我建议您始终在模块定义中使用./routes/user.js。肯定有帮助。

-编码愉快

评论


我认为它们不必要地复杂和混乱。它应该透明和直观。

– ngungo
2014年4月27日,1:11

我同意。在某些情况下,它可能对命名间隔很有用,但通常不会造成任何损坏。

–科迪
2014年4月28日在1:52

#10 楼

要了解差异,您必须首先了解Node.js在运行时对每个模块的作用。 Node.js为每个模块创建一个包装函数:

  (function(exports, require, module, __filename, __dirname) {

 })()
 


注意第一个参数exports是一个空对象,第三个参数module是具有许多属性的对象,并且其中一个属性名为exports。这就是exports的来源以及module.exports的来源。前一个是变量对象,后一个是module对象的属性。

在模块中,Node.js在开始时会自动执行以下操作:module.exports = exports,最终返回module.exports。 br />
因此,您可以看到,如果将一些值重新分配给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的每次更新都将被忽略。

#11 楼

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

问题
模块不可能直接导出除导出对象(例如函数)以外的值。例如,一个模块可能只想导出其定义的对象类型的构造函数。现在,它无法执行此操作,因为require始终将其创建的exports对象用作导出的值。 module。此属性最初指向由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);


评论


我不得不在Node中重新创建它并测试一些东西,直到我明白了。基本上,为模块创建的内部函数甚至从不返回导出对象。因此,实际上并未在模块中重新分配“导出”对象,例如如果您尝试直接编写exports =“现在是字符串”。该对象仅作为参考存在。这是我直到现在才真正意识到的行为。

– danielgormly
16-2-26在4:07



#12 楼

这是

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 楼

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

var a = {},md={};


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

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

exp.attr = "change";

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


#14 楼

在docs

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

这只是指向module.exports的变量。

#15 楼

我发现此链接有助于回答上述问题。

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应该没事

评论


git链接坏了

–Jesse Hattabaugh
17年7月6日在20:18

链接现已修复。

–PawełGościcki
17年7月19日在10:26

#16 楼

“如果您希望模块导出的根是一个函数(例如构造函数),或者想一次导出一个完整的对象而不是一次构建一个属性,则将其分配给module.exports出口。” -http://nodejs.org/api/modules.html

#17 楼

在评估模块之前,module.exportsexports都指向同一对象。

当您的模块在使用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 


Node.js网站有一个非常好的对此的解释。

#18 楼

1.exports->用作单例实用程序
2。 module-exports->用作服务,模型等逻辑对象

#19 楼

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

一种方法

var aa = {
    a: () => {return 'a'},
    b: () => {return 'b'}
}

module.exports = aa;


第二种方法

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


这就是require()将如何集成模块的方式。 br />
function require(){
    module.exports = {};
    var exports = module.exports;

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

    return module.exports;
}


#20 楼


为什么都在这里使用

我相信他们只是想清楚module.exportsexportsnano指向同一个函数-允许您使用任意一个变量在内部调用函数文件。 nano提供了函数功能的上下文。
exports不会被导出(只有module.exports会被导出),那么为什么还要覆盖它呢?例如在文件中使用exports而不是module.exports。它还澄清了module.exportsexports实际上指向相同的值。


module.exports(然后将值添加到它们都引用的对象中),您将不会有任何问题,可以放心使用exports来使其更加简洁。除非您有意识地希望module.exports是特定的东西(例如函数),否则其他地方可能会造成混淆。以便可以在其他文件中使用它。比较第二个示例短了多少:

#21 楼



您创建的每个文件都是一个模块。模块是一个对象。它具有称为exports : {}的属性,默认情况下为空对象。

您可以创建函数/中间件,并将其添加到此空的导出对象中,例如exports.findById() => { ... },然后将require添加到应用程序中的任何位置并使用... >
控制器/user.js

exports.findById = () => {
    //  do something
}


在route.js中要求使用:

const {findyId} = './controllers/user'


#22 楼

节点js中的module.js文件用于运行module.load system。每次节点执行文件时,它都会如下包装您的js文件内容

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


包裹在ur js源代码中的代码,您可以访问export,require,module等。
使用此方法是因为没有其他方法可以将在js文件上写入的功能复制到另一个文件。 br />然后,节点使用c ++执行此包装函数。这时将填充传递到此函数的导出对象。

您可以在此函数内部看到参数export和module。
实际上export是模块构造函数的公共成员。

看下面的代码

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


现在使用node

运行,这是输出
是[object Object]

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

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

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

请记住

使用mo仅当您在使用require关键字时想要获得函数时,dule.exports才会打开。在上面的示例中,我们var foo = require(a.js);您可以看到我们可以将foo称为函数;

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

#23 楼




module.exportsexports都指向相同的function database_module(cfg) {...}。到b,输出反向。结论是:


aa是独立的。



所以b等效于:

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


假设上面是module.exports = exports = nano = function database_module(cfg) {...},这是module.js所必需的。 foo.js的优点现在很明显:




module.exports = exports = nano = function database_module(cfg) {...}中,因为foo.jsmodule.exportsrequire('./module.js'):您可以使用moduls.js而不是exports