2

Possible Duplicate:
setTimeout and “this” in JavaScript

I am trying to put a timeout on an Object. With some test code (see below) I want to decrease the timerPos until it reaches 0. When I use the code below the first time timerInc() is called by startTimer(), it will reach 3 (as expected). When TimerInc() is called by the timeout i will receive 'undefined' for the timerPos variable. What am I doing wrong?

function start(){
var alert = new Alert(3);
alert.startTimer();

}
function Alert(timer) {
    this.timerMinutes = timer;
    this.timerPos = 0;

    this.startTimer = function() {
        this.timerPos = this.timerMinutes+1;
        this.timerInc();
    };


    this.timerInc = function() { 
        if (this.timerPos > 0){     
            this.timerPos--;   
                    // first time this function gets called timerPos is 3
                    // the second time when its called by the timeout it
                    // will be 'undefined'
            setTimeout(this.timerInc,1000);
        }   
    };
}

(using this.timerInc() in the timeout instead of this.timerInc does not work for me, neither does using quotes)

Community
  • 1
  • 1
Stupidity
  • 79
  • 1
  • 11

2 Answers2

2

You need to bind the "this" variable to another one that you use explicitly since the value of "this" changes based on who is calling the function!

function Alert(timer) {
  var that = this; // Store this Alert instance as "that".
  this.timerMinutes = timer;
  this.timerPos = 0;

  // ...
  this.timerInc = function() { 
    // Use the Alert instance "that", not whatever is bound to "this" at runtime.
    if (that.timerPos > 0){     
      that.timerPos--;
      setTimeout(that.timerInc, 1000);
    }
  };
}

The issue is that the setTimeout() function will call its function argument from global scope, not the scope of the enclosing object at the time it is registered. So in global scope the "this" variable is bound to the "global" object (likely the browser window).

You can verify like so:

setTimeout(function(){alert(this);}, 500); // => alerts "[object DOMWindow]"
maerics
  • 151,642
  • 46
  • 269
  • 291
1

First of all you should use prototype to declare the methods of your class Alert. And changing the scope of the function you're calling is gonna do the job:

function start(){
var alert = new Alert(3);
alert.startTimer();

}
function Alert(timer) {
    this.timerMinutes = timer;
    this.timerPos = 0;
}

Alert.prototype.startTimer = function() {
        this.timerPos = this.timerMinutes+1;
        this.timerInc();
    };


Alert.prototype.timerInc = function() { 
    if (this.timerPos > 0){     
        this.timerPos--;
        console.log(this.timerPos);
        setTimeout(function(_this){_this.timerInc()},1000,this);
    }   
};

DEMO: http://jsfiddle.net/kmendes/HNYKa/1/

Karl Mendes
  • 649
  • 4
  • 9