node.js 中的 module.exports 与导出

IT技术 javascript node.js commonjs
2021-01-18 23:12:42

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

module.exports = exports = nano = function database_module(cfg) {...}

我不知道什么是之间的差异module.exportsexports为什么都被用在这里。

6个回答

尽管很久以前就已经回答并接受了问题,但我只想分享我的 2 美分:

你可以想象在你的文件的最开始有这样的东西(只是为了解释):

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

在此处输入图片说明

因此,无论您做什么,请记住,当您从其他地方需要该module时module.exports,不会exports从您的module返回。

因此,当您执行以下操作时:

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

您正在添加 2 个函数ab指向的对象module.exports,因此typeof返回结果将是object{ a: [Function], b: [Function] }

当然,如果您module.exports在本示例中使用exports.

在这种情况下,您希望自己module.exports的行为像一个导出值的容器。然而,如果您只想导出一个构造函数,那么您应该了解使用module.exportsor 的一些知识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

因为 with exports,引用不再指向指向的对象module.exports,所以exports之间module.exports不再有关系在这种情况下module.exports仍然指向{}将返回的空对象

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

那么,使用的意义exports何在?module.exports如果只是变量重新分配,为什么不总是使用对我来说似乎很困惑。
2021-03-10 23:12:42
很好的解释,但我仍然不明白如何module.exports从module中完全省略,例如在这个npm包中:github.com/tj/consolidate.js/blob/master/lib/consolidate.js
2021-03-13 23:12:42
oooo 最后这个答案解释了它。基本上导出是指您可以向其添加属性的对象,但是如果您重新分配给函数,那么您就不再将属性附加到该原始对象上。现在 export 引用函数,而 module.exports 仍然指向该对象,因为它是返回的内容。你可以说出口基本上已经被垃圾收集了。
2021-03-15 23:12:42
@Imray 解释在这里:JavaScript 是否通过引用传递? exports.a = function(){}; works, exports = function(){} doesn't work
2021-03-25 23:12:42
@jedd.ahyoung 写起来exports.something那么麻烦module.exports.something
2021-04-09 23:12:42

设置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.exportsexports以确保exports不引用先前导出的对象。通过将两者都设置exports为速记,并避免以后出现潜在的错误。

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

@Asad 是的,如果您设置,该功能将正确导出 module.exports
2021-03-16 23:12:42
@Liam 感谢您的宝贵回答。更多查询 - 在 server.js 的入口处,module.exports 和exports 的值应该是多少?module.exports 是否预期为空并将导出设置为空对象?这是遗留问题还是有一些有效的用例可以将导出和 module.exports 指向两个不同的对象?
2021-03-16 23:12:42
@ajostergaard:它恰好是OP 示例取自名称在module中,它允许作者编写诸如nano.version = '3.3'代替 之类的东西module.exports.version = '3.3',这样读起来更清晰一些。(请注意,这nano是一个局部变量,在设置module导出之前声明了一点。)
2021-03-22 23:12:42
嘿 Lime,这是一个很旧的答案,但我希望你能澄清一些事情。如果我要设置module.exports 设置exports,我的代码还可以工作吗?谢谢你的帮助!
2021-04-01 23:12:42
@lime - 谢谢 - 我很高兴这在很大程度上无关紧要,因为如果不是,那就意味着我完全误解了一切。:-| :)
2021-04-05 23:12:42

基本上答案在于通过require语句需要module时实际发生的情况假设这是第一次需要该module。

例如:

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 = [];
}

如您所见,每个module对象都有一个名为 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从本质上讲,它为module程序员提供了便利。

但是,需要谨慎使用这种便利。在任何情况下,如果尝试将新对象分配给导出,请确保我们这样做。

exports = module.exports = {};

如果我们按照错误的方式来做module.exports仍然会指向作为module实例的一部分创建的对象。

exports = {};

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

我认为exports = module.exports = app;在代码的最后一行添加没有任何优势 似乎module.exports将被导出并且我们永远不会使用exports,因为它再次位于代码的最后一行。那么,我们为什么不简单地添加module.exports = app;
2021-03-11 23:12:42
我认为这应该是最好的答案,它解释了为什么func()@William 的答案失败了!
2021-03-13 23:12:42
在这里失去了我 exports = module.exports = {};
2021-04-06 23:12:42

最初,module.exports=exportsrequire函数返回module.exports所指的对象

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

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

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

这是迄今为止最明确的答案!如果您想为它添加书签,这是准确的链接:stackoverflow.com/questions/7137397/...
2021-03-31 23:12:42
是的,这实际上是真正的答案。它简洁明了。其他人可能是对的,但充满了花哨的术语,并没有完全专注于这个问题的答案。
2021-04-07 23:12:42

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

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

var exports = module.exports = {};

如果在您的module内重新分配exports,则您在module内重新分配它并且它不再等于module.exports这就是为什么,如果你想导出一个函数,你必须这样做:

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

如果您只是将您的function() { ... }to分配exports,您将重新分配exportsto 不再指向module.exports

如果你不想module.exports每次都引用你的函数,你可以这样做:

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

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

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

exports.foo = function() { ... }
这是所有答案中最容易理解的!
2021-03-14 23:12:42
简单明了
2021-03-18 23:12:42
了解此功能的简单方法。
2021-03-23 23:12:42