当node.js中需要一个模块时,它会多次返回相同的对象,因为require()
会缓存之前的调用。
假设我有一个主记录器模块可以注册子记录器模块。 (那些使记录实际上通过主记录器模块log()
功能。但它在这里没有关系。)
我在主记录器模块中有类似的东西来添加子模块:
module.addRedisLogger = function(rclient) {
modulesArray.push(require('./redis.js')(rclient, loggingEnabled, module));
}
当我创建一个redis客户端实例时,我可以立即向它添加一个记录器:
var sub = redis.createClient();
logger.addRedisLogger(sub);
在子模块中,我开始像这样记录:
module.startLogging = function() {
rclient.on("message", messageCallback);
rclient.on("pmessage", pmessageCallback);
}
并停止记录如下:
module.stopLogging = function() {
rclient.removeListener("message", messageCallback);
rclient.removeListener("pmessage", pmessageCallback);
}
但据我所知,使用这种技术我只能分配一个redis记录器实例,因为分配第二个将在require()
中返回与第一个相同的对象,因此传递新的{{ 1}}参数将覆盖以前的值。因此,由于调用它而无法停止第一个redis logger实例中的日志记录,因此将停止第二个实例中的日志记录。
让我们看一个例子:
redis
我除了得到这个输出:
var sub1 = redis.createClient();
var sub2 = redis.createClient();
sub1.subscribe("joinScreen1");
sub2.subscribe("joinScreen2");
logger.addRedisLogger(sub1);
logger.addRedisLogger(sub2);
// running redis-cli PUBLISH joinScreen1 message1
// running redis-cli PUBLISH joinScreen2 message2
logger.log("Lets stop the first logger);
logger.modulesArray[0].stopLogging()
// running redis-cli PUBLISH joinScreen1 message1
// running redis-cli PUBLISH joinScreen2 message2
我们应该得到这个,因为第一个记录器现在指向第二个实例。所以// Message received on channel joinScreen1: message1
// Message received on channel joinScreen2: message2
// Message received on channel joinScreen1: message1
也指向第二个客户端。
但我得到了这个:
redis
因此它可以按计划设计的预期工作,但不像CODE预期的那样。所以我想让它现在正常工作,但我不明白为什么它会这样。
更新
长版
module.js
// Message received on channel joinScreen1: message1
// Message received on channel joinScreen2: message2
// Message received on channel joinScreen2: message2
main.js
var util = require("util");
module.exports = function () {
var module = {};
module.show = function() {
console.log(util.client);
}
module.set = function(value) {
util.client= value;
}
return module;
};
运行var util = require("util");
util.client = "waaaa";
var obj = require('./module')();
obj.show();
obj.set("weeee");
console.log(util.client);
将导致此输出:
main.js
所以C:\Users\me\Desktop>node main.js
waaaa
weeee
给出了与第一次完全相同的对象。如果我从那时起改变了它,那么改变也在那里,因为它是同一个对象。
现在我们可以说变量require()
与redis
相同,并保留对redis连接的引用。当构造函数第二次运行时它会覆盖第一个,这就是为什么我要从第一个redis客户端的记录器获取通知,因为没有引用指向它,因此无法删除监听器。 / p>
答案 0 :(得分:3)
就像你说的那样,需要缓存require
调用的RETURNED对象。看看你的用例,其中require只返回一个function
对象,就是这样。调用此函数时,会改变内存中的某些引用,因此它可以按预期工作。缓存函数与缓存该函数调用的输出不同。此外,返回构造函数是节点中公开类的惯用方法,因为每次需要新对象时,您只需调用该函数即可。换句话说,你正在做的事情非常好。
离。
案例A
module.exports = new Date();
案例B
module.exports = function() {
return new Date();
}
在A的情况下,require将缓存日期对象,并始终返回对同一个的引用。在B的情况下,require将缓存函数对象,并始终返回对同一个的引用。不同的是,在B中,当调用该缓存函数时,该函数每次都会返回一个新的Date对象。
答案 1 :(得分:2)
当你这样说时:
但据我所知,使用这种技术我只能分配一个redis记录器实例,因为分配第二个将在require()中返回与第一个相同的对象,因此传递新的redis参数将覆盖以前的值。
这不是你的情况。您的模块正在导出单个函数:
module.exports = function (rclient, ploggingEnabled, logger) {
多次调用require('./redis.js')
将每次都返回此功能。确实如此。如果您修改该功能的属性,然后再次require()
,您确实会看到您所做的修改:
(require('./redis.js')).x = "waaa";
console.log((require('./redis.js')).x); // "waaa"
但是当你实际调用函数而不是修改它的属性时,会发生不同的事情:通过声明startLogging()
和stopLogging()
函数来创建closure,每个函数都操作更高的变量在scope chain中。这意味着每次调用此导出函数时,都会创建一个新的闭包,其中包含指向rclient
和ploggingEnabled
以及logger
的私有指针。如果你保存对这个函数返回的值的引用(你通过push()
将其放到modulesArray
上),那么你应该能够在以后访问每个闭包并执行任何操作你需要清理:
modulesArray.forEach(function(el, idx, arr) {
el.stopLogging();
});