当需要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()
会返回与第一次相同的对象。 如果从那以后我改变了,那么改变也在那里,因为它是同一个对象。
现在让我们说variablesredis
和client
是一样的,并且保持对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一个新的闭包,并使用它自己的指向rclient
和ploggingEnabled
和logger
的专用指针。 如果保存对此函数返回的值的引用(通过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对象。