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

当node.js中需要多个模块时,它会返回相同的对象,因为require()caching以前的调用。

比方说,我有一个主要的logging模块可以注册子logging模块。 (这些logging实际上是通过主logging器模块的log()函数实现的,但是这里没有关系)。

我在主logging器模块中添加了一个子模块:

 module.addRedisLogger = function(rclient) { modulesArray.push(require('./redis.js')(rclient, loggingEnabled, module)); } 

当我创build一个redis客户端实例时,我可以立即添加一个logging器,如下所示:

 var sub = redis.createClient(); logger.addRedisLogger(sub); 

在子模块中,我开始logging如下:

 module.startLogging = function() { rclient.on("message", messageCallback); rclient.on("pmessage", pmessageCallback); } 

并停止这样的日志logging:

 module.stopLogging = function() { rclient.removeListener("message", messageCallback); rclient.removeListener("pmessage", pmessageCallback); } 

但据我所知,用这种技术,我只能分配一个redislogging器实例,因为分配第二个将在require()返回与第一个相同的对象,所以传递新的redis参数将覆盖以前的值。 因为这样就不可能停止第一个redis logger实例的日志logging,因为调用它会停止第二个日志logging。

让我们看一个例子:

 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 

我们应该得到这个,因为第一个logging器现在指向第二个实例。 所以redis指向第二个客户端。

但是,我得到这个:

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

所以它按照程序devise的预期工作,但不像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()会返回与第一次相同的对象。 如果从那以后我改变了,那么改变也在那里,因为它是同一个对象。

现在让我们说variablesredisclient是一样的,并且保持对redis连接的引用。 当构造函数第二次运行时,它将覆盖第一个,这就是为什么我除了从第一个redis客户端的logging器获取通知,因为没有引用指向它,所以侦听器无法删除。

当你这样说的时候:

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

这不是你的情况。 你的模块正在导出一个单一的function:

 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()函数来创build闭包 ,其中每个函数都会操纵作用域链中较高的variables。 这意味着每次调用这个导出的函数时,都会创build一个新的闭包,并使用它自己的指向rclientploggingEnabledlogger的专用指针。 如果保存对此函数返回的值的引用(通过push()它推送到modulesArray ),那么您应该能够在稍后访问每个闭包,并执行所需的清理:

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

就像你说的那样,要求cachingrequire调用的RETURNED对象。 看看你的用例,只需要返回一个function对象,就是这样。 这个函数在被调用时会改变内存中的一些引用,因此它可以按照预期工作。 caching一个函数,不同于caching该函数的调用输出。 另外,返回一个构造函数是节点暴露类的惯用方法,因为每当你需要一个新的对象时,你可以直接调用这个函数。 换句话说,你在做什么是完美的。

恩。

案例A

 module.exports = new Date(); 

案例B

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

在情况A中,require将cachingdate对象,并将始终返回对同一个对象的引用。 在情况B中,require会caching函数对象,并且总是返回一个对同一个对象的引用。 不同之处在于,在调用caching函数时,函数每次都会返回一个新的Date对象。