繁体   English   中英

当在node.js中需要相同的模块时,require()如何工作

[英]How require() works when requiring the same module in node.js

当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()返回与第一个相同的对象,因此传递新的redis参数将覆盖之前的值。 因此,由于调用它而无法停止第一个redis logger实例中的日志记录,因此将停止第二个实例中的日志记录。

让我们看一个例子:

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指向第二个客户端。

但相反,我得到了这个:

// Message received on channel joinScreen1: message1
// Message received on channel joinScreen2: message2
// Message received on channel joinScreen2: message2

所以它按程序设计的预期工作,但不像CODE预期的那样。 所以我想让它现在正常工作,但我不明白为什么它会这样。

更新:

长版

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

main.js

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()会给出与第一次相同的对象。 如果我从那时起改变了它,那么改变也在那里,因为它是同一个对象。

现在假设变量redisclient在这里相同,并保持对redis连接的引用。 当构造函数第二次运行时它会覆盖第一个,这就是为什么我要从第一个redis客户端的记录器获取通知,因为没有引用指向它,因此无法删除监听器。

就像你说的那样,需要缓存require调用的RETURNED对象。 看看你的用例,其中require只是返回一个function对象,就是这样。 调用此函数时,会改变内存中的某些引用,因此它可以按预期工作。 缓存函数与缓存该函数调用的输出不同。 此外,返回构造函数是节点中公开类的惯用方法,因为每次需要新对象时,您只需调用该函数即可。 换句话说,你正在做的事情非常好。

恩。

案例A.

module.exports = new Date();

案例B

module.exports = function() {
  return new Date();
}

在A的情况下,require将缓存日期对象,并始终返回对同一个的引用。 在B的情况下,require将缓存函数对象,并始终返回对同一个的引用。 区别在于,在B中,当调用该缓存函数时,该函数每次都会返回一个新的Date对象。

当你这样说:

但据我所知,使用这种技术我只能分配一个redis记录器实例,因为分配第二个将在require()中返回与第一个相同的对象,因此传递新的redis参数将覆盖之前的值。

这不是你的情况。 您的模块正在导出单个函数:

module.exports = function (rclient, ploggingEnabled, logger) {

require('./redis.js')调用require('./redis.js')将每次返回此函数。 确实如此。 如果您修改该函数的属性,然后再次require()它,您确实会看到您所做的修改:

(require('./redis.js')).x = "waaa";
console.log((require('./redis.js')).x); // "waaa"

但是当你实际调用函数而不是修改它的属性时,会发生一些不同的事情:通过声明startLogging()stopLogging()函数来创建闭包 ,每个函数都操作范围链中较高的变量。 这意味着每次调用此导出函数时,都会创建一个新的闭包,它有自己的指向rclientploggingEnabledlogger的私有指针。 如果你保存对这个函数返回的值的引用(你可以通过push()它引入modulesArray ),那么你应该能够在以后访问每个闭包并执行你需要的任何清理:

modulesArray.forEach(function(el, idx, arr) {
  el.stopLogging();
});

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM