什么是module.exports vs exports之间的模块和区别?

我已经阅读了几个小时这个话题,只是没有find任何东西来帮助这个坚持。

一个模块只是一个具有几个属性的节点中的对象,一个是引用对象的exports属性。

“出口”variables是

var exports = module.exports 

这是一个var指向module.exports引用的对象。

我正在努力的是可视化模块是什么。 我知道这是一个对象,但只有一个?

我知道这不是节点实现一个模块的确切方式,但我看起来像这样:

 var module = {} module.exports = {} // now module has a property module.exports var exports = module.exports 

现在,从我读过的所有东西,如果你要分配一些东西到module.export ='xyz'

它将保持“xyz”的值。 它失去了原来的对象? 最重要的是,如果我在同一个文件中给module.exports分配了其他内容,它会被新的值replace吗?

 EX: // file = app.js module.export = 'hello' module.export = 'bye' // file = newApp.js require(./app); 

模块的价值是什么? 我重写相同的模块对象还是有多个?

在继续之前,重要的是要了解模块如何实际按节点加载 。

从节点的模块加载系统中取走的关键是,在它实际运行所需的代码之前(发生在Module#_compile )之前,它会创build一个新的空exports对象作为Module一个属性。 (换句话说,你的可视化是正确的。)

Node然后用匿名函数将文本包装在require d文件中:

 (function (exports, require, module, __filename, __dirname) { // here goes what's in your js file }); 

…基本上是eval那个string。 eval该string是一个函数(匿名包装器)的结果,并且节点立即调用该函数,传递参数如下:

 evaledFn(module.exports, require, module, filename, dirname); 

requirefilenamedirname是对require函数(实际上不是全局函数)的引用,以及包含已加载模块的path信息的string。 (这是文档的意思,当他们说“ __filename实际上不是一个全局的,而是每个模块本地的。 ”)

所以我们可以看到在一个模块里面,确实exports === module.exports 。 但为什么模块同时获得一个module和一个exports

向后兼容。 在节点的最初几天, module内部没有modulevariables。 你只是分配给exports 。 但是,这意味着你永远不能将构造函数导出为模块本身。

作为模块导出构造函数的模块的熟悉示例:

 var express = require('express'); var app = express(); 

这是因为Express通过重新分配module.exports输出一个函数 ,覆盖默认情况下节点给你的空对象:

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

但是,请注意,您不能只写:

 exports = function { ... } 

这是因为JavaScript的parameter passing语义很奇怪 。 从技术上讲,JavaScript可能被认为是“纯粹按价值传递”,但实际上参数是通过“按价值参照”传递的。

这意味着当你将一个对象传递给一个函数时,它接收到一个对该对象的引用作为一个值。 您可以通过该引用访问原始对象,但不能更改调用者对引用看法。 换句话说,就像你在C / C ++ / C#中看到的那样,“out”参数是没有的。

作为一个具体的例子:

 var obj = { x: 1 }; function A(o) { ox = 2; } function B(o) { o = { x: 2 }; } 

调用A(obj); 将导致obj.x == 2 ,因为我们访问传入的原始对象为o 。 但是, B(obj); 什么都不会做 obj.x == 1 。 给B的本地o分配一个全新的对象只会改变o指向的地方。 它对调用者的对象obj没有任何影响,它仍然不受影响。

现在应该很明显,为什么需要将module对象添加到节点模块的本地范围。 为了允许模块完全replaceexports对象,它必须作为一个对象传递给模块匿名函数的属性来使用。 显然没有人想破坏现有的代码,因此exports保留为对module.exports的引用。

所以当你只是将属性赋值给你的exports对象时,无论你使用exports还是module.exports都没有关系, 他们是一个一样的,一个参考指向完全相同的对象。

只有当你想导出一个函数作为顶级导出,你必须使用module.exports ,因为正如我们所看到的,简单地分配一个函数exports不会超出模块范围。

最后要说明的是,当你将一个函数作为模块导出时,分配给exportsmodule.exports是一个很好的习惯。 这样,两个variables都保持一致,并且与标准模块中的variables一样。

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

请务必在模块文件的顶部附近执行此操作,以免任何人不小心将其分配给exports ,最终导致覆盖。

另外,如果这对你来说看起来很奇怪( = s?),我利用了包含赋值运算符的expression式返回赋值的事实,这使得可以在一个镜头中将单个值赋给多个variables。

Node.js中的模块只是文件。 每个文件都是一个模块,每个模块都是一个文件。 如果您有多个文件,则可以有多个模块(每个文件一个)。

根据module.exports : Node.js API文档将阐明这个主题:

module.exports对象由Module系统创build。 有时候这是不可接受的。 许多人希望他们的模块成为某个类的一个实例。 为此,将所需的导出对象分配给module.exports 。 请注意,将所需对象分配给exports将简单地重新绑定本地exportsvariables,这可能不是您想要的。

模块中可用的exportsvariables作为对module.exports的引用而module.exports 。 和任何variables一样,如果给它赋一个新的值,它不再被绑定到前一个值。 …作为一个指导,如果出口和module.exports之间的关系对你来说似乎很神奇,那么忽略导出并且只使用module.exports。

那么,这意味着什么? 在你的特定例子中, module.exports将等于它的最后分配值( 'bye' )。

你重写模块 – 导出是一个单一的对象被拉入require 。 通常,当使用require执行这种模块化JavaScript时,在您的示例中,您的导出将是构造函数而不是像string一样的原始对象。 这样,您可以创build模块中定义的新function实例。 例:

 // module.js var MyConstructor = function(prop) { this.prop = prop; }); module.exports = MyConstructor; // app.js var MyConstructor = require('module'); var instance = new MyConstructor('test'); console.log(instance.prop) // 'test'