我几个小时以来一直在阅读这个主题,而且还没有发现任何可以帮助制作这个主题的内容。
模块只是节点中的一个对象,具有一些属性,一个是引用对象的exports属性。
'exports'变量是
var exports = module.exports
它是指向module.exports引用的对象的var。
我正在努力想象的是模块是什么。我知道这是一个对象,但只有一个吗?
我知道这不是节点实现模块的确切方式,但我可视化它看起来像这样:
var module = {}
module.exports = {}
// now module has a property module.exports
var exports = module.exports
现在,从我一直在阅读的所有内容中,如果你要为module.export ='xyz'分配一些东西
它将保持值'xyz'。它会丢失原始对象吗?最重要的是,如果我在同一个文件中为module.exports分配了其他内容,是否会将其替换为新值?
EX:
// file = app.js
module.export = 'hello'
module.export = 'bye'
// file = newApp.js
require(./app);
模块的价值是多少?我是否覆盖了相同的模块对象,还是有多个?
答案 0 :(得分:7)
在我们继续之前,了解how modules are actually loaded by node非常重要。
从节点的模块加载系统中取消的关键是,在它实际运行代码之前require
(在Module#_compile
中发生),它creates a新的,空的exports
对象作为Module
的属性。 (换句话说,您的可视化是正确的。)
然后使用匿名函数节点wraps require
d文件中的文本:
(function (exports, require, module, __filename, __dirname) {
// here goes what's in your js file
});
......基本上eval
是那个字符串。 eval
该字符串的结果是一个函数(匿名包装器),并且节点立即调用函数,传入如下参数:
evaledFn(module.exports, require, module, filename, dirname);
require
,filename
和dirname
是对require
函数(实际为isn't a global)的引用,以及包含已加载模块的字符串&#39 ; s路径信息。 (这就是docs的含义,当他们说" __filename
实际上并非全局而是每个模块都是本地的。")
所以我们可以在模块中看到exports === module.exports
。但为什么模块同时获得module
和exports
?
向后兼容性。在节点的早期阶段,模块内部没有module
变量。您只需分配到exports
即可。但是,这意味着您永远不能将构造函数导出为模块本身。
一个熟悉的模块导出构造函数作为模块的例子:
var express = require('express');
var app = express();
这是因为Express exports a function by reassigning module.exports
,会覆盖节点默认提供的空对象:
module.exports = function() { ... }
但请注意,您不能写:
exports = function { ... }
这是因为JavaScript's parameter passing semantics are weird。从技术上讲,JavaScript可能被视为"纯粹的按值传递,"但实际上参数是通过"按值引用"。
这意味着当您将对象传递给函数时,它会将该对象的引用作为值接收。您可以通过该引用访问原始对象,但无法更改调用者对引用的感知。换句话说,没有" out"你可能会在C / C ++ / C#中看到param。
作为一个具体的例子:
var obj = { x: 1 };
function A(o) {
o.x = 2;
}
function B(o) {
o = { x: 2 };
}
调用A(obj);
将导致obj.x == 2
,因为我们访问了以o
传入的原始对象。但是,B(obj);
将无所作为; obj.x == 1
。将全新对象分配给B
的本地o
只会更改o
指向的内容。它对呼叫者的对象obj
没有任何作用,它仍然不受影响。
现在应该明白为什么因此需要将module
对象添加到节点模块'当地范围。为了允许模块完全替换exports
对象,它必须作为对象传递给模块匿名函数的属性。显然,没有人想破坏现有代码,因此exports
被留作module.exports
的引用。
因此,当您只为导出对象分配属性时,使用exports
还是module.exports
并不重要;它们是同一个,是指向完全相同的对象的参考。
仅当您要将某个功能导出为必须使用module.exports
的顶级导出时,才会这样做,因为正如我们所见,只需指定exports
的函数在模块范围之外没有任何影响。
最后请注意,当您将某个功能导出为模块时,最好将分配给 exports
和module.exports
。这样,两个变量保持一致,并且与标准模块中的变量一样。
exports = module.exports = function() { ... }
请务必在模块文件的顶部附近执行此操作,以便不会有人意外地分配到最终被覆盖的exports
。
另外,如果你看起来很奇怪(三 =
s?),我会利用包含assignment operator的表达式返回的事实指定的值,可以在一次拍摄中为单个值分配单个值。
答案 1 :(得分:2)
你正在覆盖模块 - 导出是一个单独的对象被require
拉入。通常,在使用require
执行此类模块化JavaScript时,导出将是构造函数,而不是示例中的字符串。这样,您就可以创建在模块中定义的新功能实例。例如:
// 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'
答案 2 :(得分:2)
Node.js中的模块只是文件。每个文件都是一个模块,每个模块都是一个文件。如果您有多个文件,则可以有多个模块(每个文件一个)。
根据module.exports
:the Node.js API documentation将阐明该主题:
module.exports
对象由Module系统创建。有时 这是不可接受的;许多人希望他们的模块成为一个实例 一些课。为此,请将所需的导出对象指定给module.exports
。请注意,将所需对象分配给exports
将会 只需重新绑定本地exports
变量,这可能不是什么 你想做什么。
和
模块中可用的
exports
变量以a开头 参考module.exports
。与任何变量一样,如果您指定一个新变量 它的值,它不再绑定到以前的值。 ......作为指导, 如果export和module.exports之间的关系 对你来说似乎很神奇,忽略导出并只使用module.exports。
那么,这一切意味着什么?在您的特定示例中,module.exports
将等于其最后指定的值('bye'
)。