node.exports与Node.js中的导出

And*_*rle 701 javascript commonjs node.js

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

module.exports = exports = nano = function database_module(cfg) {...}
Run Code Online (Sandbox Code Playgroud)

我不知道什么之间的不同module.exportsexports为什么都被用在这里.

Srl*_*rle 473

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

您可以想象在文件的最开头有类似的东西(仅用于解释):

var module = new Module(...);
var exports = module.exports;
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

所以无论你做什么,只要记住,当你从其他地方要求那个模块时module.exports,不会exports从你的模块返回.

所以当你做的事情如下:

exports.a = function() {
    console.log("a");
}
exports.b = function() {
    console.log("b");
}
Run Code Online (Sandbox Code Playgroud)

您正在向module.exports指向的对象添加2个函数'a'和'b',因此a返回的结果将是b:module.exports

当然,如果您typeof在此示例中使用而不是,则会得到相同的结果object.

在这种情况下,您希望module.exports的行为类似于导出值的容器.然而,如果你只想导出构造函数,那么你应该知道使用{ a: [Function], b: [Function] }or module.exports;(再次记住,当你需要某些东西时,会返回module.exports,而不是导出).

module.exports = function Something() {
    console.log('bla bla');
}
Run Code Online (Sandbox Code Playgroud)

现在返回结果的类型是exports,您可以要求它并立即调用:
module.exports因为您将返回的结果覆盖为函数.

但是,使用module.exports你不能使用类似的东西:

exports = function Something() {
    console.log('bla bla');
}
var x = require('./file1.js')(); //Error: require is not a function
Run Code Online (Sandbox Code Playgroud)

因为exports,该参考文献没有"点"了到物体,其中module.exports分,所以没有之间的关系exporttypeof了.在这种情况下,module.exports仍然指向'function'将返回的空对象.

另一个主题的接受答案也应该有所帮助: Javascript是否通过引用传递?

  • oooo最后这个答案解释了它.基本上导出是指您可以添加属性的对象,但是如果您将其重新分配给它起作用,那么您不会将属性附加到该原始对象.现在导出引用函数,而module.exports仍然指向该对象,因为它是返回的内容.你可以说导出基本上是垃圾收集. (24认同)
  • @Imray解释如下:[JavaScript是否通过引用传递?](http://stackoverflow.com/questions/13104494/does-javascript-pass-by-reference)`exports.a = function(){}; works,exports = function(){}不起作用 (4认同)
  • 那么,使用`exports`有什么意义呢?如果它只是一个变量重新分配,为什么不总是使用`module.exports`?似乎让我很困惑. (4认同)
  • 很好的解释,但是我仍然不明白如何从模块中完全省略`module.exports`,例如在以下`npm`包中:https://github.com/tj/consolidate.js/blob/master/ lib / consolidate.js (2认同)
  • @jedd.ahyoung 编写 `exports.something` 而不是 `module.exports.something` 没那么麻烦 (2认同)
  • @jedd.ahyoung 通过向 `exports` 添加属性,您可以有效地确保返回“典型”模块导出 *object*。相比之下,通过使用`module.exports`,你可以返回任何你想要的值(原语、数组、函数),而不仅仅是一个对象(这是大多数人期望的格式)。所以`module.exports` 提供了更多的功能,但也可以用来让你的模块导出非典型值(如原语)。相比之下,`exports` 限制性更强,但更安全(只要你简单地向它添加属性而不是重新分配它)。 (2认同)
  • 这个答案为那些不理解其他答案的人澄清了一些事情。从头开始说。 (2认同)
  • 对我来说,这是点击的答案。为简洁的解释点赞。我建议大家创建一个节点项目,并不断记录对象 `exports` 和 `module.exports` 以查看其行为。 (2认同)

Lim*_*ime 418

设置module.exports允许database_module函数像函数一样被调用required.简单设置exports将不允许导出函数,因为节点导出对象module.exports引用.以下代码不允许用户调用该函数.

module.js

以下方法无效.

exports = nano = function database_module(cfg) {return;}
Run Code Online (Sandbox Code Playgroud)

如果module.exports设置,以下将起作用.

module.exports = exports = nano = function database_module(cfg) {return;}
Run Code Online (Sandbox Code Playgroud)

安慰

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

基本上,node.js不会导出exports当前引用的对象,但会导出exports最初引用的属性.虽然Node.js确实导出了对象module.exports引用,但允许您像函数一样调用它.


第二重要的原因

它们设置了两者module.exportsexports确保exports不引用先前导出的对象.通过将两者都设置exports为速记,并在以后的路上避免潜在的错误.

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

  • @ajostergaard:恰好是[库](https://github.com/dscape/nano/blob/master/nano.js)的名称,OP的例子来自于.在该模块中,它允许作者编写诸如`nano.version ='3.3'`之类的东西,而不是`module.exports.version ='3.3'`,它读得更清楚一些.(注意`nano`是一个局部变量,[在模块导出设置之前稍微声明](https://github.com/dscape/nano/blob/3.3.7/nano.js#L17-45). ) (8认同)
  • @lime - 谢谢 - 我很高兴这在很大程度上是无关紧要的,因为如果不是,那就意味着我完全误解了一切.: - | :) (3认同)

Cha*_*ndu 206

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

例如:

var x = require('file1.js');
Run Code Online (Sandbox Code Playgroud)

file1.js的内容:

module.exports = '123';
Run Code Online (Sandbox Code Playgroud)

执行上述语句时,将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 = [];
}
Run Code Online (Sandbox Code Playgroud)

如您所见,每个模块对象都有一个名称属性exports.这是最终作为一部分返回的内容require.

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

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

并且以下面的方式调用此匿名函数,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");
Run Code Online (Sandbox Code Playgroud)

正如我们在函数内部所看到的,exports正式参数指的是module.exports.实质上,它为模块程序员提供了便利.

然而,这种便利需要谨慎行事.在任何情况下,如果尝试将新对象分配给导出,请确保我们这样做.

exports = module.exports = {};
Run Code Online (Sandbox Code Playgroud)

如果我们按照错误方式执行,module.exports仍将指向作为模块实例的一部分创建的对象.

exports = {};
Run Code Online (Sandbox Code Playgroud)

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

  • 在这里迷失了我`exports = module.exports = {};` (8认同)
  • 我认为这应该是最好的答案,它解释了为什么`func()`在@William的回答中失败了! (2认同)
  • 我看不到在代码的最后一行添加`exports = module.exports = app;`没有任何好处。看来`module.exports`将被导出,我们将永远不会使用`exports`,因为它再次位于代码的最后一行。那么,为什么不简单地添加`module.exports = app;`呢? (2认同)

cam*_*ron 78

最初,module.exports=exportsrequire函数返回对象module.exports引用.

如果我们向对象添加属性,比如说exports.a=1,那么module.exports和exports 仍然引用同一个对象.因此,如果我们调用require并将模块分配给变量,那么变量的属性为a,其值为1;

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

对我来说,我将继续添加新属性,或将它们都覆盖到新对象.只是覆盖一个是不对的.请记住,这module.exports是真正的老板.

  • 我认为"你是真正的老板":)非常感谢 (5认同)
  • 是的,这实际上是真正的答案。它简洁明了。其他人可能是对的,但充满了花哨的术语,并没有完全专注于这个问题的答案。 (3认同)

dus*_*ltz 52

exports并且module.exports是相同的,除非你重新分配exports你的模块中.

考虑它的最简单方法是认为这一行隐含在每个模块的顶部.

var exports = module.exports = {};
Run Code Online (Sandbox Code Playgroud)

如果在您的模块中重新分配exports,则在模块中重新分配它,它不再等于module.exports.这就是为什么,如果要导出函数,必须执行以下操作:

module.exports = function() { ... }
Run Code Online (Sandbox Code Playgroud)

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

如果您不想module.exports每次都参考您的功能,您可以:

module.exports = exports = function() { ... }
Run Code Online (Sandbox Code Playgroud)

请注意,这module.exports是最左边的参数.

附加属性exports不一样,因为您没有重新分配它.这就是为什么这样的原因

exports.foo = function() { ... }
Run Code Online (Sandbox Code Playgroud)

  • 这是所有答案中最容易理解的! (8认同)
  • 很好,很直白 (2认同)

sup*_*ary 26

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

这与在JavaScript中通过引用传递对象的方式有细微差别.

exports并且module.exports都指向同一个对象.exports是一个变量,module.exports是模块对象的一个​​属性.

说我写这样的东西:

exports = {a:1};
module.exports = {b:12};
Run Code Online (Sandbox Code Playgroud)

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

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

  • 最佳答案imho! (6认同)

小智 12

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

var module.exports = {};
var exports = module.exports;
Run Code Online (Sandbox Code Playgroud)

所以:

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.
Run Code Online (Sandbox Code Playgroud)

2:

exports.abc = function(){}; // works!
exports.efg = function(){}; // works!
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)


Sal*_*lar 10

以下是关于Manning出版物行动手册中node.js中节点模块的详细描述. 最终在您的应用程序中导出的是module.exports.exports只是作为module.exports的全局引用设置,最初定义为可以添加属性的空对象.所以exports.myFunc是刚刚速记module.exports.myFunc. 因此,如果将exports设置为其他任何内容,则会破坏module.exportsexports之间 的引用.因为module.exports是真正导出的,导出将不再按预期工作 - 它不再引用模块.exports.如果要维护该链接,可以再次使module.exports 引用导出,如下所示:



module.exports = exports = db;
Run Code Online (Sandbox Code Playgroud)


Cod*_*ody 7

我经历了一些测试,我认为这可能会对这个问题有所了解......

app.js:

var ...
  , routes = require('./routes')
  ...;
...
console.log('@routes', routes);
...
Run Code Online (Sandbox Code Playgroud)

版本/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] }"
Run Code Online (Sandbox Code Playgroud)

我甚至添加了新文件:

./routes/index.js:

module.exports = require('./not-index.js');
module.exports = require('./user.js');
Run Code Online (Sandbox Code Playgroud)

./routes/not-index.js:

exports = function fn(){};
Run Code Online (Sandbox Code Playgroud)

./routes/user.js:

exports = function user(){};
Run Code Online (Sandbox Code Playgroud)

我们得到输出"@routes {}"


./routes/index.js:

module.exports.fn = require('./not-index.js');
module.exports.user = require('./user.js');
Run Code Online (Sandbox Code Playgroud)

./routes/not-index.js:

exports = function fn(){};
Run Code Online (Sandbox Code Playgroud)

./routes/user.js:

exports = function user(){};
Run Code Online (Sandbox Code Playgroud)

我们得到输出"@routes {fn:{},user:{}}"


./routes/index.js:

module.exports.fn = require('./not-index.js');
module.exports.user = require('./user.js');
Run Code Online (Sandbox Code Playgroud)

./routes/not-index.js:

exports.fn = function fn(){};
Run Code Online (Sandbox Code Playgroud)

./routes/user.js:

exports.user = function user(){};
Run Code Online (Sandbox Code Playgroud)

我们得到输出"@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');
Run Code Online (Sandbox Code Playgroud)

./routes/not-index.js:

exports.fn = function fn(){};
Run Code Online (Sandbox Code Playgroud)

./routes/user.js:

exports.ThisLoadedLast = function ThisLoadedLast(){};
Run Code Online (Sandbox Code Playgroud)

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

所以我建议总是module.exports在你的模块定义中使用.

我不完全理解Node内部发生了什么,但如果你能更好地理解这一点,请发表评论,因为我确信它有所帮助.

- 快乐的编码


qin*_*127 6

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

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

 })()
Run Code Online (Sandbox Code Playgroud)

请注意,第一个参数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) // {}
Run Code Online (Sandbox Code Playgroud)

但是如果你更新了 的属性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 }
Run Code Online (Sandbox Code Playgroud)

另请注意,如果您将另一个值重新分配给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')}
Run Code Online (Sandbox Code Playgroud)