2012-07-09 24 views
7

Tengo un problema que todavía me molesta en js oop - Estoy seguro de que lo estoy haciendo mal, pero no puedo entender cómo hacerlo bien.Javascript OOP - perdido esto en la devolución de llamada asincrónica

Por ejemplo, tengo este código

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.'); 
     } 
    } 
} 

El problema es que no puedo acceder a la función setToken a partir del contexto de la función "request.onloadend" - es probablemente porque he perdido la referencia a "este".

¿Cuál es la solución a este problema? ¿De alguna manera puedo pasar el "este" var al contexto de esta función?

Gracias!

Respuesta

4

Hay un par de formas de hacer esto. El más directo es simplemente guardar una copia del valor que necesita:

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.'); 
     } 
    } 
} 

Otra forma es utilizar bind:

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

El segundo enfoque es más "limpia", pero tiene problemas de compatibilidad del navegador (IE < 9 no lo admite).

+0

Creo que el otro es más directo, el código se modifica mínimamente y no es necesario que decida qué palabra clave pseudo utilizar. Solo digo. – Esailija

+0

@Esailija: Yo también, pero lamentablemente su valor práctico se ve limitado por la compatibilidad del navegador. – Jon

+0

Punto discutible con 'XHR.onloaded' – Esailija

1

Usted sólo puede capturar una referencia a él en el perímetro exterior, he utilizado el identificador self, sin embargo, no dude en darle el nombre de un significado más semántica:

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

captura this antes de la devolución de llamada:

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 la función:

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

Guardar this en una var fuera de la devolución de llamada.

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

tiene toda la razón: la devolución de llamada se llama con el objeto XMLHTTPRequest según el contexto (es decir, el valor de this). Es necesario dar la instancia de otro nombre, para que pueda acceder a ella dentro del alcance de la devolución de llamada:

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. He usado authInstance en lugar de self, porque creo que generalmente es bueno usar nombres de variables descriptivos; nunca tendrá que averiguar qué significa authInstance, mientras que self podría ser ambiguo cuando alguien en el futuro (¡posiblemente usted!) lea el código.

Otra opción es usar bind, pero eso es probablemente más complicado de lo necesario aquí.

+0

Modificar la mayor parte del código y usar 2" palabras clave "separadas, así como una variable adicional es menos complicado que simplemente lanzar' .bind (this) 'al ¿fin? – Esailija

+0

@Esailija Creo que la forma 'bind' es menos intuitiva y legible. El contexto solo se vuelve claro al final de la función, y la función es bastante larga. Si fuera una función de dos líneas, estaría de acuerdo contigo. – lonesomeday

+0

Dentro de una clase 'this' que se refiere a la instancia actual de la clase ([no importa cómo se implemente el enlace] (http://en.wikipedia.org/wiki/Dynamic_dispatch)), es más intuitivo en mi opinión. Pero podemos estar en desacuerdo sobre eso. – Esailija

Cuestiones relacionadas