2015-09-15 41 views
6

Wenn ein Modul mehr als einmal in node.js benötigt wird, gibt es dasselbe Objekt zurück, weil require() die vorherigen Aufrufe zwischenspeichert.Wie require() funktioniert, wenn dasselbe Modul in node.js benötigt wird

Nehmen wir an, ich habe ein Hauptlogger-Modul, was die Sub-Logger-Module registrieren könnte. (Diejenigen, machen die Protokollierung tatsächlich durch den Hauptlogger Modul log() Funktion Aber nicht hier im Zusammenhang..)

Ich habe so etwas wie dies im Hauptlogger Modul eine Sub-Modul hinzuzufügen:

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

Wenn ich erstelle ein redis Client-Instanz, konnte ich sofort einen Logger, um es wie folgt hinzu:

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

im Submodul beginne ich wie folgt anmelden:

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

Und wie diese Anmeldung stoppen:

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

} 

Aber soweit ich es verstehe, mit dieser Technik konnte ich nur eine redis Logger Instanz zuweisen, da die zweite Zuordnung in den require() mit dem gleichen Objekt zurückkehren würde als Im ersten Fall überschreibt der neue Parameter redis den vorherigen Wert. Aus diesem Grund ist es nicht möglich, die Protokollierung in der ersten Redologger-Instanz zu stoppen, da das Aufrufen in der zweiten Instanz die Protokollierung stoppt.

sich ein Beispiel sehen:

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 

ich außer diese Ausgabe zu erhalten:

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

Das sollten wir bekommen, weil der erste Logger nun auf die zweite Instanz verweist. Die redis verweist also auch auf den zweiten Client.

Aber stattdessen bekomme ich dies:

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

So funktioniert es wie von PROGRAM DESIGN erwartet, aber nicht von CODE erwartet. Also möchte ich es so machen, wie es jetzt funktioniert, aber ich verstehe nicht, warum es so funktioniert.

UPDATE:

Long Version

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); 

die main.js Laufen wird diese Ausgabe zur Folge hat:

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

So require() gibt das gleiche Objekt wie das erste Mal zurück. Wenn ich es seitdem geändert habe, dann sind die Änderungen auch da, weil es das gleiche Objekt ist.

Nehmen wir an, die Variable redis ist hier identisch mit client und enthält einen Verweis auf eine Redis-Verbindung. Wenn der Konstruktor das zweite Mal ausgeführt wird, überschreibt er den ersten, weshalb ich die Benachrichtigungen vom Logger des ersten redis client abrufe, weil keine Referenz darauf zeigt, so dass der Listener nicht entfernt werden konnte.

+0

Ich habe es schwer, eine Frage aus dem Post Inhalt zu entschlüsseln. Die Frage im Titel ist wahrscheinlich ein Duplikat, aber ich glaube nicht, dass du das fragst, du scheinst bereits zu wissen, wie require() funktioniert und cacht. Am Ende wird dies ein logisches Problem sein, und die Logik (Code) ist nicht in der Post so organisiert, dass wir sie leicht verfolgen können (zu viel fehlt). –

+0

Ich habe meine Frage aktualisiert, ich hoffe, dass es jetzt verständlicher ist. – NoNameProvided

Antwort

2

, wenn Sie sagen, dass dies:

Aber soweit ich mit dieser Technik verstehen, konnte ich nur eine redis Logger Instanz zuweisen, da die zweite Zuordnung in der require() mit dem gleichen Objekt zurückkehren würde als in der ersten würde also die Übergabe des neuen redis-Parameters den vorherigen Wert überschreiben.

das ist nicht, was in Ihrem Fall passiert. Ihr Modul exportiert eine einzige Funktion:

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

Aufruf require('./redis.js') mehr als einmal wird diese Funktion jedes Mal zurück. Das ist richtig. Und wenn Sie Eigenschaften dieser Funktion ändern, dann require() es wieder, werden Sie in der Tat die Änderungen sehen Sie gemacht:

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

Aber wenn Sie tatsächlich die Funktion aufrufen, anstatt seine Eigenschaften zu modifizieren, etwas anderes geschieht: ein closure ist erstellt durch Deklarieren der startLogging() und stopLogging() Funktionen, von denen jede Variablen weiter oben in der scope chain manipulieren.Dies bedeutet, dass jedes Mal, wenn Sie diese exportierte Funktion aufrufen, eine neue Schließung mit eigenen privaten Zeigern zu rclient und ploggingEnabled und logger erstellt wird. Wenn Sie eine Referenz auf den von dieser Funktion zurückgegebenen Wert speichern (was Sie tun, indem Sie push() auf modulesArray setzen), dann sollten Sie in der Lage sein, auf jeden dieser Verschlüsse zu einem späteren Zeitpunkt zuzugreifen und die von Ihnen benötigte Bereinigung durchzuführen:

3

Wie Sie bereits gesagt haben, erfordern Caches das Objekt RETURNED des Aufrufs require. Werfen Sie einen Blick in Ihren Anwendungsfall, in dem die Anforderung lediglich ein function Objekt zurückgibt, das ist alles. Diese Funktion mutiert, wenn sie aufgerufen wird, einige Referenzen im Speicher, daher funktioniert sie wie erwartet. Das Zwischenspeichern einer Funktion entspricht nicht dem Zwischenspeichern der Ausgabe des Aufrufs dieser Funktion. Darüber hinaus ist die Rückgabe einer Konstruktorfunktion der idiomatische Weg, um Klassen in Klassen zu exponieren, da Sie jedes Mal, wenn Sie ein neues Objekt benötigen, einfach die Funktion aufrufen können. Mit anderen Worten, was du machst, ist vollkommen in Ordnung.

ex.

Fall A

module.exports = new Date(); 

Fall B

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

In Fall A, erfordert das Datum Objekt-Cache und wird immer eine Referenz auf die gleiche zurückzukehren. In Fall B require required wird das Funktionsobjekt zwischenspeichern und gibt immer einen Verweis auf denselben zurück. Der Unterschied besteht darin, dass die Funktion in B beim Aufruf dieser zwischengespeicherten Funktion jedes Mal ein neues Date-Objekt zurückgibt.

+0

Ich habe meine Frage aktualisiert, und ich brauche einige Erläuterungen zu Ihrer Antwort. Ich bekomme es, dass, wenn ich es als eine Funktion exportiere, es jedes Mal laufen wird, aber es mir immer noch das Samb-Objekt gibt, richtig? also dies tun, 'var module = Anfrage (" Modul "); module.x = "y"; console.log (Anfrage ("Modul"). X); ' wird das Drucken des" y "auf der Konsole richtig? Ich möchte ein neues Objekt bekommen, was nicht den definierten x-Parameter hat. – NoNameProvided

+0

Das ist richtig, es sollte "y" drucken. Warum denkst du, dass es noch etwas anderes sein sollte? Die Variable 'module' ist ein Zeiger auf' request ("modul") '. Wenn Sie also die Eigenschaft' x' ändern, wird diese Änderung unweigerlich in 'request (" module ")' propagiert, da beide Zeiger auf EXACT sind gleiches Objekt. –

+0

Ja, aber ich mache dasselbe in meinem realen Beispiel und ich bekomme ein anderes Ergebnis. Sehen Sie den aktualisierten Teil (lange Version) meiner Frage. – NoNameProvided

Verwandte Themen