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