2010-05-01 7 views
53

Ich brauche this von meinem setInterval Handler zugreifenJavascript setInterval und `this` Lösung

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

Wie kann ich this.prefs in ajax.onload zugreifen?

Antwort

79

Die setInterval Linie sollte wie folgt aussehen: -

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

bearbeiten: Das gleiche Prinzip gilt für die "onload". In diesem Fall ist es üblich, dass der "äußere" Code wenig tut, er richtet nur die Anfrage ein und sendet sie dann. In diesem Fall ist der zusätzliche Overhead einer zusätzlichen Funktion wie in dem obigen Code unnötig. Ihre retrieve_rate sollte wie folgt aussehen: -

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

Ich wollte dies zunächst tun, aber dann erinnerte ich mich, dass dieses Muster wirklich sehr nützlich für Schleifen ist. –

+0

@Matthew Flaschen: Es ist für dieses Szenario genauso nützlich wie für Schleifen. –

+0

@Anthony: also ist der Trick mit 'self' die einzige Option hier? Kannst du bestätigen, dass die Lösung von Matthew nicht funktioniert? – Pablo

-1

Das ist nicht eine Schönheit Lösung ist, aber es ist im allgemeinen Sprachgebrauch:

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

Das Problem ist, wie 'setInterval' ruft die' retrieve_rate' Funktion, die ' Dieser Wert innerhalb der Methode bezieht sich auf das globale Objekt ... – 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' in der Funktion, die an' setInterval' übergeben wird, bezieht sich auf das globale Objekt. Meinst du "context.retrieve_rate" anstatt "this.retrieve_rate"? – CMS

+0

Danke für die Entdeckung, CMS. –

+0

Dies hat sich in die richtige Richtung entwickelt, der Kontext muss jedoch nicht als Parameter übergeben werden. – AnthonyWJones

18

Das Standardverhalten von setInterval auf den globalen Kontext zu binden. Sie können eine Elementfunktion aufrufen, indem Sie eine Kopie des aktuellen Kontexts speichern. Innerhalb von retrieve_rate wird die this Variable korrekt an den ursprünglichen Kontext gebunden. Hier ist, was Ihr Code aussehen würde:

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

Bonus-Tipp

: Für eine reine Funktionsreferenz (im Gegensatz zu einer Objektreferenz gegenüber, die eine Elementfunktion hat) können Sie den Kontext ändern, indem Sie JavaScript die call oder apply Methoden.

+1

Das funktionierte bei mir, der Aufruf 'call' scheint aber nicht nötig zu sein. Der Kontext von retrieve_rate sollte standardmäßig auf self gesetzt sein, da er als Memberfunktion aufgerufen wird. – Dreendle

+0

@Dreendle - Sie haben Recht, ich erinnere mich, dies für eine Callback-Funktion Referenz zu lösen, wo es benötigt wurde. Ich habe die Antwort behoben, danke! –

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

Dies ist die richtige Lösung. Die akzeptierte Lösung scheint unnötig mehr Code zu benötigen. – theoutlander

+4

Aber diese Methode hat einen Nachteil. Höchstwahrscheinlich wird es nicht mit älteren Versionen von IE – Nechehin

+0

@Nechehin beachten. Aber es ist immer noch eine viel sauberere Lösung. – connorbode

1

Dies wäre die sauberste Lösung, da die meiste Zeit tatsächlich schalten Sie die diesen Kontext für Ihre aufeinander folgenden Methodenaufrufe wollen: von

Auch ist es einfacher zu begreifen, das Konzept.

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

Es funktionierte für mich, indem ich 'that.myMethod()' aufruft – meyer1994

8

Mit Verbesserung der Browser die Zeit unterstützen, ist jetzt gut, die EcmaScript 6 Erweiterung zu verwenden, auf den Pfeil => Methode, this richtig zu bewahren.

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

Verwenden => Verfahren bewahrt die this wenn retrieve_rate() durch das Intervall genannt wird. Keine Notwendigkeit für flippige selbst oder this in Parameter

vorbei
5

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

dies legal ist in JavaScript und spart viel Code :)