2010-05-01 17 views
53

necesito para acceder a this de mi setInterval manejadorJavascript setInterval y `this` solución

prefs: null, 
startup : function() 
    { 
     // init prefs 
     ... 
     this.retrieve_rate(); 
     this.intervalID = setInterval(this.retrieve_rate, this.INTERVAL); 
    }, 

retrieve_rate : function() 
    { 
     var ajax = null; 
     ajax = new XMLHttpRequest(); 
     ajax.open('GET', 'http://xyz.com', true); 
     ajax.onload = function() 
     { 
      // access prefs here 
     } 
    } 

cómo puedo acceder a this.prefs en ajax.onload?

Respuesta

79

La línea setInterval debería tener este aspecto: -

this.intervalID = setInterval(
    (function(self) {   //Self-executing func which takes 'this' as self 
     return function() { //Return a function in the context of 'self' 
      self.retrieve_rate(); //Thing you wanted to run as non-window 'this' 
     } 
    })(this), 
    this.INTERVAL  //normal interval, 'this' scope not impacted here. 
); 

Editar: El mismo principio se aplica a las el "onload". En este caso es común que el código "externo" haga poco, simplemente configura la solicitud y luego la envía. En este caso, la sobrecarga adicional es innecesaria una función adicional como en el código anterior. Su retrieve_rate debe mirar más a esto: -

retrieve_rate : function() 
{ 
    var self = this; 
    var ajax = new XMLHttpRequest(); 
    ajax.open('GET', 'http://xyz.com', true); 
    ajax.onreadystatechanged= function() 
    { 
     if (ajax.readyState == 4 && ajax.status == 200) 
     { 
      // prefs available as self.prefs 
     } 
    } 
    ajax.send(null); 
} 
+0

Iba a hacer esto inicialmente, pero luego recordé que este patrón es realmente más útil para los bucles. –

+0

@Matthew Flaschen: Es tan útil para este escenario como para los bucles. –

+0

@Anthony: ¿así que el truco con 'self' es la única opción aquí? ¿Puedes confirmar que la solución de Matthew no funcionará? – Pablo

-1

Eso no es una solución belleza, sino que es de uso común:

var self = this; 
var ajax = null; 
//... 
ajax.onload = function() { 
    self.prefs....; 
} 
+2

El problema es cómo '' setInterval' llama a la función retrieve_rate', el ' este 'valor dentro del método se refiere al objeto global ... – CMS

0
prefs: null, 
startup : function() 
    { 
     // init prefs 
     ... 
     this.retrieve_rate(); 
     var context = this; 
     this.intervalID = setInterval(function() 
             { 
              context.retrieve_rate(); 
             }, this.INTERVAL); 
    }, 

retrieve_rate : function() 
    { 
     var ajax = null; 
     ajax = new XMLHttpRequest(); 
     ajax.open('GET', 'http://xyz.com', true); 
     var context = this; 
     ajax.onload = function() 
     { 
      // access prefs using context. 
      // e.g. context.prefs 
     } 
    } 
+0

' this' dentro de la función pasada a 'setInterval', se referirá al objeto global. ¿Querías decir 'context.retrieve_rate' en lugar de' this.retrieve_rate'? – CMS

+0

Gracias por detectar eso, CMS. –

+0

Esto ha evolucionado en la dirección correcta, sin embargo, no necesita que se pase el contexto como parámetro. – AnthonyWJones

18

El comportamiento predeterminado de setInterval es para enlazar con el contexto global. Puede llamar a una función miembro guardando una copia del contexto actual. Dentro de retrieve_rate, la variable this se vinculará correctamente al contexto original. Esto es lo que el código se vería así:

var self = this; 
this.intervalID = setInterval(
    function() { self.retrieve_rate(); }, 
    this.INTERVAL); 

Consejo adicional: Para una referencia a una función normal (en comparación con una referencia de objeto que tiene una función miembro) puede cambiar el contexto mediante el uso de call o apply métodos de JavaScript.

+1

Esto funcionó para mí, aunque la llamada a 'call' no parece ser necesaria. El contexto de retrieve_rate se debe establecer en self de forma predeterminada, porque se llama como una función miembro. – Dreendle

+0

@Dreendle - tiene razón, recordé resolver esto para una referencia de función de devolución de llamada donde fuera necesario. He arreglado la respuesta, ¡gracias! –

64
this.intervalID = setInterval(this.retrieve_rate.bind(this), this.INTERVAL); 
+10

Esta es la solución correcta. La solución aceptada parece requerir innecesariamente más código. – theoutlander

+4

Pero este método tiene un inconveniente. Lo más probable es que no funcione con versiones anteriores de IE – Nechehin

+0

@Nechehin Worth noteing. Pero sigue siendo una solución mucho más limpia. – connorbode

1

Esta sería la solución más limpia, ya que la mayoría de las veces que realmente quieren cambiar el este contexto para el método consecutiva llama:

También es más fácil de entender el concepto de.

// store scope reference for our delegating method 
    var that = this; 
    setInterval(function() { 
     // this would be changed here because of method scope, 
     // but we still have a reference to that 
     OURMETHODNAME.call(that); 
    }, 200); 
+0

Me funcionó al llamar a 'that.myMethod()' – meyer1994

8

Con la mejora del navegador admite ahora es el momento bueno utilizar la mejora EcmaScript 6, el método flecha =>, para preservar this correctamente.

startup : function() 
    { 
     // init prefs 
     ... 
     this.retrieve_rate(); 
     this.intervalID = setInterval(() => this.retrieve_rate(), this.INTERVAL); 
    }, 

Usando => método preserva la this cuando retrieve_rate() es llamado por el intervalo. No hay necesidad de auto funky o pasar this en los parámetros

5

window.setInterval(function(){console.log(this)}.bind(this), 100)

esto es legal en javascript y ahorra un montón de código :)

Cuestiones relacionadas