2015-09-15 5 views
6

node.jsにモジュールが複数回必要な場合は、同じオブジェクトを戻します(require()は前の呼び出しをキャッシュします)。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); 

} 

しかし、これまで私が理解として、二番目を割り当てることと同じオブジェクトでrequire()に戻ってくるため、この技術を用いて、私は一つだけRedisのロガーインスタンスを割り当てることができます新しいredisパラメータを渡すと、前の値が上書きされます。そのため、最初のredis loggerインスタンスを呼び出すので、2番目のインスタンスのログを停止するので、最初の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も第2のクライアントを指しています。

しかし、その代わりに、私はこれを取得:

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

だから、期待通りにプログラム設計で動作しますが、コードによって期待できないとして。だから私はそれが今働くように動作させたいが、なぜそれがそのように機能するのか分からない。

UPDATE:

ロングバージョン

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を実行するJS

var util = require("util"); 
util.client = "waaaa"; 

var obj = require('./module')(); 

obj.show(); 
obj.set("weeee"); 

console.log(util.client); 

C:\Users\me\Desktop>node main.js 
waaaa 
weeee 

だから require()は初めてのような非常に同じオブジェクトをバック与えます。それ以来私がそれを変更した場合、同じオブジェクトであるため変更もそこにあります。

ここで、変数redisはここではclientと同じで、redis接続への参照を保持しているとします。コンストラクタが2回目に実行されると、それが最初のものを上書きします。そのため、最初のredisクライアントのロガーから通知を受け取ることを除いて、リスナーを削除できませんでした。

+0

私は投稿内容から質問を解読するのに苦労しています。タイトルの質問は重複している可能性が高いですが、私はそれがあなたが要求しているとは思わない、あなたはすでにrequire()の作品とキャッシュを知っているようです。結局のところ、これは論理的な問題になり、ロジック(コード)は簡単に追跡できるようにポストに編成されていません(あまりにも多くは欠落しています)。 –

+0

私の質問が更新されました。 – NoNameProvided

答えて

2

あなたはこの言う:

しかし、これまで私が理解として、二番目を割り当てることと同じオブジェクトを必要と()に戻ってくるため、この技術を用いて、私は一つだけRedisのロガーインスタンスを割り当てることができますが最初に新しいredisパラメータを渡すと、以前の値が上書きされます。

あなたのケースでは起こっていないことです。

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

は一度この機能を毎回返しますより多くのrequire('./redis.js')を呼び出す:あなたのモジュールは、単一の関数をエクスポートしています。それは本当だ。そして、あなたはその関数のプロパティを変更する場合は、require()それは再び、あなたは確かにあなたが行った変更が表示されます:

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

をしかし、あなたはなく、実際にそのプロパティを変更する機能を呼び出す際に、別の何かが起こります。closureですstartLogging()stopLogging()関数を宣言して作成したもので、それぞれがscope chainの上位の変数を操作します。これは、このエクスポートされた関数を呼び出すたびに、rclientploggingEnabledloggerへの専用のポインタを持つ新しいクロージャが作成されることを意味します。この関数によって返された値に参照を保存した場合(push()modulesArrayに設定した場合)、後でこれらのクロージャにアクセスして、必要なクリーンアップを実行できます。

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

あなたが言ったように、requireコールのRETURNEDオブジェクトをキャッシュする必要があります。あなたのユースケースを見てみましょう。必要なものはfunctionオブジェクトを返すだけです。それだけです。この関数は、呼び出されるとメモリ内の一部の参照を変更するため、期待通りに機能します。関数をキャッシュすることは、その関数の呼び出しの出力をキャッシュすることと同じではありません。さらに、コンストラクタ関数を返すことは、新しいオブジェクトが必要なたびに関数を呼び出すことができるたびに、クラスを公開するという慣用的な方法です。言い換えれば、あなたがやっていることはまったく問題ありません。

ex。

ケース

module.exports = new Date(); 

ケースBケースAでは

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

は、日付オブジェクトをキャッシュする必要があり、常に同じものへの参照を返します。ケースBの場合、requireは関数オブジェクトをキャッシュし、常に同じものへの参照を返します。違いは、Bでは、そのキャッシュされた関数を呼び出すとき、その関数は毎回新しいDateオブジェクトを返します。

+0

私の質問が更新されました。あなたの答えを明確にする必要があります。私はそれを関数としてエクスポートすると毎回実行されますが、それでも私はsambオブジェクトを正しく返しますか?これを行うと、 'var module = request(" module "); module.x = "y"; console.log(request( "module")。x); ' コンソールに「y」が表示されますか?私は定義されたxパラメータを持たない新しいオブジェクトを取得したい。 – NoNameProvided

+0

それは正しいです、それは "y"を印刷する必要があります。なぜあなたはそれが他のものと同等であるべきだと思いますか?変数 'module'は' request( "module") 'へのポインタなので、プロパティ' x'を変更すると、両方ともEXACTへのポインタであるため、 'request(" module ")'への変更を必然的に伝えるでしょう。同じオブジェクト。 –

+0

はい、私は実際の例でも同じことをしていますが、私は別の結果を得ています。私の質問の更新された部分(長いバージョン)を参照してください。 – NoNameProvided