2012-04-14 9 views
2

Ich versuche, die Daten in meiner Datenbank als "Klassen" in serverseitigem JavaScript (Node) zu modellieren. Für den Moment verwende ich einfach das traditionelle Konstruktormuster mit Prototypmethoden, wo angemessen und die Konstruktorfunktion als das gesamte module.exports auszusetzen. Die Tatsache, dass dies Server-Seite ist, ist für den Kern der Frage unwichtig, aber ich dachte, ich würde es als Referenz zur Verfügung stellen.Modifizieren von Instanzvariablen aus innerer Funktion in einer Prototyp-Methode

Der Problembereich der Code sieht wie folgt aus:

User.prototype.populate = function() { 
    var that = this;  
    db.collection("Users").findOne({email : that.email}, function(err, doc){ 
     if(!err) { 
      that.firstName = doc.firstName; 
      that.lastName = doc.lastName; 
      that.password = doc.password; 
     } 
     console.log(that); //expected result 
    }); 
    console.log(that); //maintains initial values 
}; 

Jedes Mal, wenn ich diese Funktion aufrufen, Änderungen an dem Objekt nicht bestehen bleiben, wenn die findOne() abgeschlossen hat. Ich stelle fest, dass der Geltungsbereich von this ändert sich auf das globale Objekt mit neuen Funktionsbereichen, so dass ich seine Referenz als that beibehalten. Wenn console.log(that) von der anonymen Funktion aus, werden die Daten wie erwartet in den Eigenschaften angezeigt. Wenn ich jedoch that protokolliere, sobald die Funktion beendet ist, behält sie den Zustand bei, den sie zu Beginn der Funktion hatte.

Was passiert hier und wie kann ich Instanzvariablen wie erwartet ändern?

Antwort

2

"Allerdings, wenn ich log, dass, sobald die Funktion beendet ist, ..."

Damit ich nehme an, Sie so etwas wie dies zu tun ...

var user = new User 

user.populate(); 

console.log(user); 

Wenn dies der Fall ist, wird console.log lange ausgeführt, bevor der asynchrone Rückruf an .findOne() aufgerufen wurde.

Jeder Code, der auf der Antwort auf findOne beruht, muss innerhalb des Rückrufs aufgerufen werden.


EDIT: Ihr Update ist ein wenig anders aus meinem Beispiel oben, aber der Grund ist das gleiche.

Der vollständige Grund für das Übergeben eines Rückrufs an die findOne-Methode ist, dass es eine asynchrone Aktivität ausführt. Wenn nicht, gibt es keinen Grund für den Rückruf. Sie würden den inneren Code direkt nach dem Anruf an findOne platzieren, wie Sie mit der console.log() getan haben.

Aber weil es asynchron ist, wartet der nachfolgende Code nicht auf Ausführung. Aus diesem Grund erhalten Sie das unbestückte Objekt in der Konsole.

Wenn Sie jedem console.log() eine Beschriftung hinzufügen, sehen Sie, dass sie nicht in der richtigen Reihenfolge ausgeführt werden.


var that = this;  
db.collection("Users").findOne({email : that.email}, function(err, doc){ 
    if(!err) { 
     that.firstName = doc.firstName; 
     that.lastName = doc.lastName; 
     that.password = doc.password; 
    } 
    console.log("inside the callback", that); // this happens Last!!! 
}); 

console.log("outside the callback", that); // this happens First!!! 

So wird deutlich, sobald man Beobachter die Reihenfolge der console.log fordert, dass die leere vor dem eine in der Rückruf geschieht.


EDIT: Sie können auch Ihre .populate() Methode einen Rückruf erhalten haben, die innerhalb des .findOne Rückruf aufgerufen wird.

User.prototype.createNickName = function() { 
    this.nickname = this.firstName.slice(0,3) + '_' + this.lastName.slice(0,3); 
}; 

    // >>>------------------------------v----receive a function argument... 
User.prototype.populate = function(callback_func) { 
    var that = this;  
    db.collection("Users").findOne({email : that.email}, function(err, doc){ 
     if(!err) { 
      that.firstName = doc.firstName; 
      that.lastName = doc.lastName; 
      that.password = doc.password; 
     } 

      // all users will have the "createNickName()" method invoked 
     that.createNickName(); 

      // ...and invoke it in the callback. 
     callback_func.call(that); 

      // By using .call(), I'm setting the "this" value 
      // of callback_func to whatever is passed as the first argument 
    }); 
}; 

// this user wants to log the firstName property in all caps 
var user1 = new User; 

user1.populate(function() { 
    console.log(this.firstName.toUpperCase()); 
}); 


    // this user wants to log the the whole name 
var user2 = new User; 

user2.populate(function() { 
    console.log(this.firstName + ' ' + this.lastName); 
}); 


    // this user wants to verify that the password is sufficiently secure 
var user3 = new User; 

user3.populate(function() { 
    console.log(verify_password(this.password)); 
}); 
+0

Das ist richtig. Dies geschieht jedoch sowohl bei einer externen Aufrufprotokollierung user.populate() als auch innerhalb der populate() -Methode selbst (jedoch außerhalb des Geltungsbereichs von findOne()). Ich habe den Beitrag aktualisiert, um zu reflektieren, was ich meinte. – DuxPrime

+0

@ user574930: Aktualisieren Sie Ihren Browser. Ich habe meine Antwort bereits aktualisiert. –

+0

Mit diesem Wissen, wie würde ich dann in der Lage sein, den Effekt zu erreichen, nach dem ich suche, wo ich einfach die populate() Methode aufrufen kann und weiß, dass die Eigenschaften des Objekts aktualisiert werden? – DuxPrime

Verwandte Themen