2012-07-09 5 views
7

Ich habe Probleme, die mich immer noch stört auf js oop - Ich bin mir sicher, ich mache es schlecht, aber ich kann nicht, wie es richtig geht.Javascript OOP - verloren in asynchronen Rückruf

Zum Beispiel, ich habe diesen Code

Auth.prototype.auth = function() { 
    var request = new XMLHttpRequest(); 

    request.open('GET', this.getAuthServerURL() + '/token', true); 
    request.send(); 

    request.onloadend = function() { 
     var response = JSON.parse(request.responseText); 

     console.log(response); 
     if(response.result == 'found') { 
     var token = response.token; 

     this.setToken(token); 
     this.isSigned = true; 
     } else { 
     console.log('Not logged yet.'); 
     } 
    } 
} 

Das Problem ist, dass ich Zugang kippe setToken aus dem Kontext von „request.onloadend“ -Funktion funktioniert - es ist wahrscheinlich, weil ich Bezug verloren „dieses“.

Was ist eine Lösung für dieses Problem? Kann ich die "This" Var irgendwie an den Kontext dieser Funktion übergeben?

Danke!

Antwort

4

Es gibt ein paar Möglichkeiten, dies zu tun. Der direkteste ist, einfach eine Kopie des Werts sparen Sie brauchen:

Auth.prototype.auth = function() { 
    var request = new XMLHttpRequest(); 
    var self = this; // save "this" value 

    request.open('GET', this.getAuthServerURL() + '/token', true); 
    request.send(); 

    request.onloadend = function() { 
     var response = JSON.parse(request.responseText); 

     console.log(response); 
     if(response.result == 'found') { 
     var token = response.token; 

     self.setToken(token); // use saved "this" value 
     self.isSigned = true; 
     } else { 
     console.log('Not logged yet.'); 
     } 
    } 
} 

Ein anderer Weg ist bind zu verwenden:

request.onloadend = (function() { 
    var response = JSON.parse(request.responseText); 

    console.log(response); 
    if(response.result == 'found') { 
    var token = response.token; 

    this.setToken(token); // use saved "this" value 
    this.isSigned = true; 
    } else { 
    console.log('Not logged yet.'); 
    } 
}).bind(this); 

Der zweite Ansatz ist „sauberer“, aber es Browser Kompatibilitätsprobleme hat (IE < 9 unterstützt dies nicht).

+0

Ich denke, das andere ist direkter, der Code ist minimal geändert und Sie müssen nicht entscheiden, welches Pseudo-Schlüsselwort zu verwenden. Sag es einfach. – Esailija

+0

@Esailija: Ich auch, aber leider ist der praktische Nutzen durch die Browserkompatibilität eingeschränkt. – Jon

+0

Moot Punkt mit 'XHR.onloaded' – Esailija

1

Sie können einen Verweis auf sie in dem äußeren Umfang nur erfassen, ich die Kennung self verwendet haben, fühlen sich aber bitte frei, den Namen eine semantische Bedeutung zu geben:

var self = this; 
request.onloadend = function() { 
    ... 
    self.setToken(token); 
    ... 
}; 
1

Capture-this vor dem Rückruf:

Auth.prototype.auth = function() { 
    var self = this; 

    var request = new XMLHttpRequest(); 

    request.open('GET', this.getAuthServerURL() + '/token', true); 
    request.send(); 

    request.onloadend = function() { 
     var response = JSON.parse(request.responseText); 

     console.log(response); 
     if(response.result == 'found') { 
     var token = response.token; 

     self.setToken(token); 
     self.isSigned = true; 
     } else { 
     console.log('Not logged yet.'); 
     } 
    } 
} 
2

.bind die Funktion:

Auth.prototype.auth = function() { 
    var request = new XMLHttpRequest(); 

    request.open('GET', this.getAuthServerURL() + '/token', true); 
    request.send(); 

    request.onloadend = function() { 
     var response = JSON.parse(request.responseText); 

     console.log(response); 
     if(response.result == 'found') { 
     var token = response.token; 

     this.setToken(token); 
     this.isSigned = true; 
     } else { 
     console.log('Not logged yet.'); 
     } 
    }.bind(this); //<-- bound 
} 
0

Speicher this in einem lokalen var außerhalb des Rückrufs.

Auth.prototype.auth = function() { 
    var request = new XMLHttpRequest(); 
    var _this = this; 

    request.open('GET', this.getAuthServerURL() + '/token', true); 
    request.send(); 

    request.onloadend = function() { 
     var response = JSON.parse(request.responseText); 

     console.log(response); 
     if(response.result == 'found') { 
     var token = response.token; 

     _this.setToken(token); 
     _this.isSigned = true; 
     } else { 
     console.log('Not logged yet.'); 
     } 
    } 
} 
0

Sie haben ganz recht: der Rückruf mit dem XMLHTTPRequest Objekt als Kontext genannt (das heißt der Wert von this). Sie benötigen eine Instanz einen anderen Namen zu geben, damit Sie es im Rahmen des Rückruf zugreifen:

Auth.prototype.auth = function() { 
    var request = new XMLHttpRequest(), 
     authInstance = this; 

    request.open('GET', this.getAuthServerURL() + '/token', true); 
    request.send(); 

    request.onloadend = function() { 
     var response = JSON.parse(request.responseText); 

     console.log(response); 
     if(response.result == 'found') { 
     var token = response.token; 

     authInstance.setToken(token); 
     authInstance.isSigned = true; 
     } else { 
     console.log('Not logged yet.'); 
     } 
    } 
} 

See this answer to another question for more explanation of why this is necessary. Ich habe authInstance anstatt self verwendet, weil ich denke, dass es generell gut ist, beschreibende Variablennamen zu verwenden; Sie müssen nie herausfinden, was authInstance bedeutet, während self möglicherweise mehrdeutig ist, wenn jemand in der Zukunft (möglicherweise Sie!) den Code liest.

Eine andere Option ist bind, aber das ist wahrscheinlich komplizierter als notwendig hier.

+0

Ändern des größten Teils des Codes und die Verwendung von 2 separaten" Schlüsselwörter "sowie eine zusätzliche Variable ist weniger kompliziert als einfach' .bind (this) 'an werfen Ende? – Esailija

+0

@Esailija Ich denke, die 'bind' Form ist weniger intuitiv und lesbar. Der Kontext wird erst am Ende der Funktion klar und die Funktion ist ziemlich lang. Wenn es eine zweizeilige Funktion wäre, würde ich dir zustimmen. – lonesomeday

+0

Innerhalb einer Klasse 'this', die sich auf die aktuelle Instanz der Klasse bezieht ([egal wie die Bindung implementiert ist] (http://en.wikipedia.org/wiki/Dynamic_dispatch)), ist meiner Meinung nach intuitiver. Aber wir können uns darüber nicht einigen. – Esailija