2010-05-01 9 views
53

J'ai besoin d'accéder this de mon gestionnaire setIntervalJavascript setInterval et `this` solution

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

Comment puis-je accéder à this.prefs ajax.onload?

Répondre

79

La ligne setInterval devrait ressembler à ceci: -

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

Modifier: Le même principe vaut pour le "onload". Dans ce cas, il est commun pour le code "externe" de faire peu, il suffit de mettre en place la demande puis l'envoie. Dans ce cas, le surdébit supplémentaire d'une fonction supplémentaire comme dans le code ci-dessus est inutile. Votre retrieve_rate devrait ressembler à ceci: -

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

J'allais le faire au départ, mais ensuite je me suis souvenu que ce modèle est vraiment très utile pour les boucles. –

+0

@Matthew Flaschen: C'est aussi utile pour ce scénario que pour les boucles. –

+0

@Anthony: donc le truc avec 'self' est la seule option ici? Pouvez-vous confirmer que la solution de Matthew ne fonctionnera pas? – Pablo

-1

Ce n'est pas une solution de beauté, mais il est dans l'usage courant:

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

Le problème est de savoir comment 'setInterval' appelle la fonction' retrieve_rate', la ' cette valeur dans la méthode se réfère à l'objet 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' dans la fonction passée à' setInterval', se référera à l'objet global. Voulez-vous dire 'context.retrieve_rate' au lieu de' this.retrieve_rate'? – CMS

+0

Merci d'avoir remarqué cela, CMS. –

+0

Cela a évolué dans la bonne direction, mais le contexte n'est pas encore passé en paramètre. – AnthonyWJones

18

Le comportement par défaut de setInterval est de se lier au contexte mondial. Vous pouvez appeler une fonction membre en enregistrant une copie du contexte en cours. À l'intérieur de retrieve_rate, la variable this sera correctement liée au contexte d'origine. Voici ce que votre code ressemblerait à ceci:

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

Astuce Bonus: Pour une référence de fonction simple (par opposition à une référence d'objet qui a une fonction de membre), vous pouvez modifier le contexte en utilisant call ou apply méthodes de JavaScript.

+1

Cela a fonctionné pour moi, l'appel à 'appeler' ne semble pas nécessaire cependant. Le contexte de retrieve_rate doit être défini sur self par défaut, car il est appelé en tant que fonction membre. – Dreendle

+0

@Dreendle - vous avez raison, je me suis souvenu de résoudre cela pour une référence de fonction de rappel où il était nécessaire. J'ai corrigé la réponse, merci! –

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

C'est la bonne solution. La solution acceptée semble nécessiter inutilement plus de code. – theoutlander

+4

Mais cette méthode a un inconvénient. Très probablement, il ne fonctionnera pas avec les anciennes versions de IE – Nechehin

+0

@Nechehin A noter. Mais c'est toujours une solution beaucoup plus propre. – connorbode

1

Ce serait la solution la plus propre, puisque la plupart du temps que vous voulez vraiment changer le ce contexte pour la méthode consécutive appelle:

Il est également plus facile de saisir le concept 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

Cela a fonctionné pour moi en appelant 'that.myMethod()' – meyer1994

8

Avec l'amélioration visionneur le temps est maintenant bon d'utiliser l'amélioration EcmaScript 6, la méthode flèche =>, pour préserver this correctement.

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

aide => méthode préserve l'this lorsque retrieve_rate() est appelé par l'intervalle. Pas besoin d'auto funky ou passer this dans les paramètres

5

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

cela est légal en javascript et permet d'économiser beaucoup de code :)

Questions connexes