2013-08-25 4 views
0

Quelqu'un peut-il s'il vous plaît dites-moi pourquoi cela ne fonctionne pas?Minuterie dans une fermeture

(function() { 
    window.Test = {}; 
})(); 

Test.Timers = { 
    c: null, 
    startTimer: function() { c = 0; setTimeout(this.doWork, 0); }, 
    doWork: function() { 
     c++; 
     alert(c); 
     setTimeout(this.doWork, 0); 
    } 
}; 

Quand j'appelle Test.Timers.startTimer(), il alerte qu'une seule fois avec 1. Merci

+1

Parce que 'doWork' a été détaché de' this' lorsque vous passez à 'setTimeout'. Donc le second 'this.doWork' vous donne une autre valeur' this'. –

+0

Quelle serait la bonne façon de le faire? Merci. – wayofthefuture

+0

Remplacez simplement cette ligne par: 'startTimer: function() {c = 0; setTimeout (this.doWork.bind (this), 0); }, ' – jpsimons

Répondre

1

Une méthode ne "se souvient" de son propriétaire (son this); vous pouvez copier une méthode d'un objet à un autre et la traiter comme n'importe quelle autre fonction. Il n'a le bon propriétaire que lorsque vous l'appelez en utilisant la notation par points, par ex. this.doWork().

Donc, votre problème est que vous passez la fonction this.doWork-setTimeout, puis il est appelé en fonction sans connaître son propriétaire, et tout à coup son this est window au lieu de votre objet de la minuterie. Pour résoudre ce problème, vous devez garder une trace de votre this. Par exemple, vous pouvez écrire:

Test.Timers = (function() { 
    var newTimer = { 
     c: null, 
     startTimer: function() { 
      this.c = 0; 
      setTimeout(function() { newTimer.doWork(); }, 0); 
     }, 
     doWork: function() { 
      this.c++; 
      alert(this.c); 
      setTimeout(function() { newTimer.doWork(); }, 0); 
     } 
    }; 
    return newTimer; 
})(); 

ou:.

Test.Timers = (function() { 
    var startTimer = function() { 
     newTimer.c = 0; 
     setTimeout(doWork, 0); 
    }; 
    var doWork = function() { 
     newTimer.c++; 
     alert(newTimer.c); 
     setTimeout(doWork, 0); 
    }; 
    var newTimer = { 
     c: null, 
     startTimer: startTimer, 
     doWork: doWork 
    }; 
    return newTimer; 
})(); 

(Notez que je aussi changé c-this.c ou newTimer.c si nécessaire, étant donné que votre version fait référence à plusieurs reprises à window.c Notez également que dans le deuxième version, si vous n'avez pas besoin de code externe pour pouvoir accéder à c, vous pouvez le changer en variable locale, rendant les choses plus propres.)

+0

C'est tellement confus comme ça. Dans mon code, je suis allé de l'avant et remplacé «ceci» par «Test.Timers» et maintenant cela fonctionne. Pensez-vous que ma solution est une mauvaise idée? Merci. – wayofthefuture

+0

@scuzzlebuzzle: Sans plus de contexte, il est difficile d'être sûr, mais - en général, ce n'est pas la meilleure approche. (Et j'espère que, en plus de changer 'this' en' Test.Timers' si nécessaire, vous avez également changé 'c' en' Test.Timers.c' si nécessaire? Parce que sinon votre code est cassé, même si c'est " travaux".) – ruakh

0

La réponse de ruakh, je préfère l'approche suivante, moi-même:

Test.Timers = (function() { 
    var this_ = { 
     c: null, 
     startTimer: function() { this_.c = 0; setTimeout(this_.doWork, 0); }, 
     doWork: function() { 
      this_.c++; 
      alert(this_.c); 
      setTimeout(this_.doWork, 0); 
     } 
    }; 
    return this_; 
})(); 

De cette façon, le sens est clair que this_ ressemble this, et tout ce que vous avez à faire est d'obtenir utilisé pour le modèle de fermeture de faire une fonction anonyme et l'appeler tout de suite. Notez également que j'ai corrigé votre référence à c pour faire référence à this_.c au lieu d'une variable globale c. Vous pouvez également utiliser .bind() pour lier la fonction this à une chose particulière. Ceci est intégré au V8 de Chrome, au moins, et peut-être Firefox ainsi:

Test.Timers = { 
    c: null, 
    startTimer: function() { this.c = 0; setTimeout(this.doWork, 0); }, 
    doWork: function() { 
     this.c++; 
     alert(this.c); 
     setTimeout(this.doWork, 0); 
    } 
}; 
Test.Timers.startTimer = Test.Timers.startTimer.bind(Test.Timers); 
Test.Timers.doWork = Test.Timers.doWork.bind(Test.Timers);