node.exports与Node.js中的导出

问题描述 投票:659回答:20

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

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

我想知道module.exportsexports之间的差异以及为什么两者都在这里使用。

javascript node.js commonjs
20个回答
400
投票

设置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引用,但允许你像函数一样调用它。


2nd least important reason

他们设置module.exportsexports以确保exports不引用先前导出的对象。通过设置你使用exports作为速记,并避免潜在的错误后来在路上。

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


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);

3
投票

这是结果

console.log("module:");
console.log(module);

console.log("exports:");
console.log(exports);

console.log("module.exports:");
console.log(module.exports);

enter image description here

也:

if(module.exports === exports){
    console.log("YES");
}else{
    console.log("NO");
}

//YES

注意:CommonJS规范仅允许使用exports变量来公开公共成员。因此,命名导出模式是唯一真正与CommonJS规范兼容的模式。 module.exports的使用是Node.js提供的扩展,以支持更广泛的模块定义模式。


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); //{}

3
投票

来自docs

exports变量在模块的文件级范围内可用,并在评估模块之前分配module.exports的值。

它允许使用快捷方式,因此module.exports.f = ...可以更简洁地编写为exports.f = ....但是,要注意像任何变量一样,如果为导出分配了新值,它是不再绑定到module.exports:

它只是一个指向module.exports的变量。


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应该没问题


2
投票

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


1
投票

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


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;
}

1
投票

为什么两个都在这里使用

我相信他们只是想明确module.exportsexportsnano指向相同的功能 - 允许您使用任一变量来调用文件中的函数。 nano为函数提供了一些上下文。

exports不会被出口(只有module.exports会),所以为什么还要打扰这个呢?

详细程度权衡限制了未来错误的风险,例如在文件中使用exports而不是module.exports。它还澄清了module.exportsexports实际上指向相同的价值。


module.exports vs exports

只要您不重新分配module.exportsexports(而是将值添加到它们都引用的对象),您将不会有任何问题并且可以安全地使用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

Why assign module.exports to a function?

更简洁!比较第二个例子的缩短程度:

helloWorld1.js: module.exports.hello = () => console.log('hello world');

app1.js:let sayHello = require('./helloWorld1'); sayHello.hello; // hello world

helloWorld2.js: module.exports = () => console.log('hello world');

app2.js:let sayHello = require('./helloWorld2'); sayHello; // hello world


0
投票

在节点js中,module.js文件用于运行module.load系统。每当节点执行一个文件时它包装你的js文件内容如下

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

因为这个包装在ur js源代码中你可以访问exports,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

export是[object Object]

foo的object.keys:name是函数(){console.log('function to module exports')}函数到模块导出

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

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

记住

使用module.exports on并且仅当您在使用require关键字时想要获取函数时才使用。在上面的例子中我们var foo = require(a.js);你可以看到我们可以将foo称为函数;

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


438
投票

即使问题早已得到回答和接受,我只想分享我的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.exportsexports;(再次记住,当您需要某些东西时,将返回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指向的对象,所以exportsmodule.exports之间不再存在关系。在这种情况下,module.exports仍然指向将返回的空对象{}

另一个主题的接受答案也应该有所帮助:Does Javascript pass by reference?


0
投票
  1. module.exportsexports都指向相同的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,输出相反。结论是: ab是独立的。
  2. 所以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.exportsrequire('./module.js')var output = require('./modules.js')(); moduls.js:您可以使用exports而不是module.exports

所以,如果exportsmodule.exports指向相同的事情,你会很高兴。


0
投票

enter image description here

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

你可以创建函数/中间件并添加到这个空的导出对象,如exports.findById() => { ... }然后require在你的应用程序的任何地方,并使用...

控制器/ user.js的

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

在routes.js中要求使用:

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

201
投票

基本上答案在于通过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的一部分导出或返回。


76
投票

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

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

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

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


46
投票

exportsmodule.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() { ... }

24
投票

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

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

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

说我写这样的东西:

exports = {a:1};
module.exports = {b:12};

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

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


11
投票

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

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

所以:

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.

2:

exports.abc = function(){}; // works!
exports.efg = function(){}; // works!

3: but, while in this case

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)

9
投票

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

module.exports = exports = db;

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内部发生了什么,但如果你能更好地理解这一点,请发表评论,因为我确信它有所帮助。

- 快乐的编码

© www.soinside.com 2019 - 2024. All rights reserved.