65073w
Last Updated: February 25, 2016
·
35.39K
· bahlor

Using `this` in scope based setTimeout / setInterval

It happens quite often that we need to define a setTimeout() or setInterval() inside a special scope like a given class. The setTimeout() / setInterval() function should call a privileged or public method of a given class and the access to this- keyword. Quite often people still try to init a timeout/interval using the following and don't succeed with it:

Wrong:

function myCounter() {
    this.i = 0;

    this.init();
}

myCounter.prototype.init = function() {
    setInterval(this.countUp,500); // fails
    setInterval(function() { this.countUp(); }, 500); // also fails, this is in a different scope
};

myCounter.prototype.countUp = function() {
    this.i++;
    document.getElementById('counter').innerHTML = this.i;
};

The above example is totally correct, except for calling the function inside the loop. Its not possible to use this.counUp() inside another scope, its not related to the desired class anymore, as soon as it gets triggered. So we have to connect a variable to our current scope by defining a new variable and assigning this to it. F.e. var _this = this; setInterval(function() { _this.countUp(); }, 500); This will work. Even in the called function, this will work fine as we are in the correct scope of our class. Now a working example:

Correct:

// basic class implementation
 function myCounter() {
     // privileged property for iteration
     this.i = 0;

     // privileged init method
     this.init();
 }

 // defining init method
 myCounter.prototype.init = function () {
     // reassign this
     var _this = this;
     setInterval(function () {
         // call this.countUp() using our new created variable.
         // this has to be done as this would normally call something 
         // inside this function, so we have to pass it as own
         // variable over
         _this.countUp();
     }, 500);
 };

 // defining the counter method
 myCounter.prototype.countUp = function () {
     this.i++;
     document.getElementById('counter').innerHTML = this.i;
 };

 // create a new instance of our counter class
 var counter = new myCounter();

jsFiddle Demo

Correct #2:

Another working example is using ECMAScript5s method bind(this, [arg1, arg2, arg3]) which allows us to bind arguments to a function, in our case we are passing this over. This is the most advanced way of doing it but also have in mind that this function is still quite new and not as widely supported as the first way of doing this. You can have a look at the compatibility table at the bottom of the following page: bind() compatibility table. All we have to do is to assign the desired function inside timeout/interval as the following setInterval(this.countUp.bind(this), 500); that's it! Piece of cake isn't it? :) Now a working example:

// basic class implementation
function myCounter() {
    // privileged property for iteration
    this.i = 0;

    // privileged init method
    this.init();
}

// defining init method
myCounter.prototype.init = function () {
    setInterval(
        // we can also use bind() to assign the correct scope
        this.countUp.bind(this)
    , 500);
};

// defining the counter method
myCounter.prototype.countUp = function () {
    this.i++;
    document.getElementById('counter').innerHTML = this.i;
};

// create a new instance of our counter class
var counter = new myCounter();

jsFiddle Demo