什么是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);
require
, filename
和dirname
是对require
函数(实际上不是全局函数)的引用,以及包含已加载模块的path信息的string。 (这是文档的意思,当他们说“ __filename
实际上不是一个全局的,而是每个模块本地的。 ”)
所以我们可以看到在一个模块里面,确实exports === module.exports
。 但为什么模块同时获得一个module
和一个exports
?
向后兼容。 在节点的最初几天, module
内部没有module
variables。 你只是分配给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
不会超出模块范围。
最后要说明的是,当你将一个函数作为模块导出时,分配给exports
和module.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
将简单地重新绑定本地exports
variables,这可能不是您想要的。
和
模块中可用的
exports
variables作为对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'