2010-05-01 9 views
53

ho bisogno di accedere this dal mio setInterval gestoreJavascript setInterval e `this` soluzione

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 
     } 
    } 

Come posso accedere this.prefs in ajax.onload?

risposta

79

La linea setInterval dovrebbe essere simile a questo: -

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

Edit: Lo stesso principio vale per il "onload". In questo caso è comune che il codice "esterno" faccia poco, imposta semplicemente la richiesta e quindi la invia. In questo caso l'overhead extra è una funzione additiva come nel codice sopra non è necessaria. Il tuo retrieve_rate dovrebbe guardare più in questo modo: -

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

Inizialmente lo avrei fatto, ma poi mi sono ricordato che questo schema è molto utile per i loop. –

+0

@Matthew Flaschen: è altrettanto utile per questo scenario quanto per i loop. –

+0

@Anthony: quindi il trucco con 'self' è l'unica opzione qui? puoi confermare che la soluzione di Matthew non funzionerà? – Pablo

-1

che non è una soluzione di bellezza, ma è di uso comune:

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

Il problema è come 'setInterval' chiama la funzione' retrieve_rate', il ' questo valore all'interno del metodo si riferisce all'oggetto globale ... – 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

'questo' all'interno della funzione passata a 'setInterval', farà riferimento all'oggetto globale. Intendevi 'context.retrieve_rate' invece di' this.retrieve_rate'? – CMS

+0

Grazie per aver individuato questo, CMS. –

+0

Questo si è evoluto nella giusta direzione, tuttavia non è necessario che il contesto venga passato come parametro. – AnthonyWJones

18

Il comportamento predefinito di setInterval è di legarsi al contesto globale. È possibile chiamare una funzione membro salvando una copia del contesto corrente. All'interno di retrieve_rate la variabile this sarà correttamente associata al contesto originale. Ecco ciò che il codice sarà simile:

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

Bonus suggerimento: Per un riferimento funzione di pianura (al contrario di un riferimento a un oggetto che ha una funzione membro) è possibile modificare il contesto utilizzando call o apply metodi di JavaScript.

+1

Questo ha funzionato per me, tuttavia la chiamata a 'call' non sembra essere necessaria. Il contesto di retrieve_rate deve essere impostato su self per impostazione predefinita, poiché viene chiamato come funzione membro. – Dreendle

+0

@Dreendle - hai ragione, mi sono ricordato di aver risolto questo problema con un riferimento alla funzione di callback dove era necessario. Ho risolto la risposta, grazie! –

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

Questa è la soluzione giusta. La soluzione accettata sembra richiedere un numero inutilmente maggiore di codice. – theoutlander

+4

Ma questo metodo ha uno svantaggio. Molto probabilmente, non funzionerà con le versioni precedenti di IE – Nechehin

+0

@Nechehin Worth noting. Ma è ancora una soluzione molto più pulita. – connorbode

1

Questa sarebbe la soluzione più pulita, poiché la maggior parte del tempo si vuole realmente cambiare il questo contesto per il metodo consecutivo chiamate:

Inoltre è più facile da afferrare il concetto di.

// 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

Ha funzionato per me chiamando 'that.myMethod()' – meyer1994

8

Con il miglioramento del browser supporta il tempo è ora bene utilizzare la valorizzazione EcmaScript 6, il metodo freccia =>, per preservare this correttamente.

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

Usando => metodo conserva la this quando retrieve_rate() viene chiamato dall'intervallo. Non c'è bisogno di auto funky o passando this nei parametri

5

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

questo è legale in javascript e fa risparmiare un sacco di codice :)