1

I have the following code that should display an incrementing number once a second:

    let timer_demo = {
        count: 0,
        timer: null,
        update: function() {
            this.count++;
            console.log(this.count);
            this.timer = setTimeout(this.update, 1000);
        }
    };
    timer_demo.update();

However, when I run this in Chrome I get a "1", then a "NaN" a second later and then nothing. The timer stops. I get the feeling the problem is that I'm not understanding what is going on with "this" in this context. I just don't know what it is. The fact that the second time the "update()" method gets invoked, the "count" field is "NaN" would seem to support this assertion. Can anybody shed some light on this for me?

Thanks!

  • 1
    `setTimeout` misses with `this`. Try: `setTimeout(this.update.bind(this), 1000);` to explicitly set `this` to be always your object. – ibrahim mahrir Aug 26 '17 at 03:02
  • 1
    that's right and it's one way to solve your problem, you can also use arrow functions setTimeout(() => this.update() , 1000); – AngelSalazar Aug 26 '17 at 03:04
  • I take it you haven't bothered even searching stack overflow, let alone the internet - there's a **lot** of information out there - e.g. https://stackoverflow.com/questions/3127429/how-does-the-this-keyword-work – Jaromanda X Aug 26 '17 at 03:05

3 Answers3

3

The function setTimeout doesn't call this.update, but rather takes a copy of the update function and repeats it. Unfortunately it loses its bind to the original "this".

To work around it, you can do the following:

let timer_demo = {
        count: 0,
        timer: null,
        update: function() {
            this.count++;
            console.log(this.count);
            this.timer = setTimeout(this.update.bind(this), 1000);
        }
    };
timer_demo.update();

This makes sure that the function copy is bound to this.

MiltoxBeyond
  • 2,545
  • 1
  • 11
  • 12
0
var timer_demo = {
        count: 0,
        timer: null,
        update: function() {
                console.log(this);
            this.count++;
            console.log(this.count);
            this.timer = setTimeout(this.update.bind(this), 1000);
        }
 };
 timer_demo.update();

In setTimeout callback this is window object by default (in non strict mode). To pass custom this to setTimeout callback, bind is used.

For better understanding of this in javascript, read http://javascriptissexy.com/understand-javascripts-this-with-clarity-and-master-it/ .

NishiJain
  • 121
  • 5
-1

You can use the new arrow functions to deal with this. They don't bind their own this, so you'll end up getting it from outside environment.

let timer_demo = {
    count: 0,
    timer: null,
    update: () => {
        this.count++;
        console.log(this.count);
        this.timer = setTimeout(this.update, 1000);
    }
};
timer_demo.update();
spanky
  • 2,648
  • 5
  • 9
  • This code doesn't work. You have the right idea about using an arrow function, but you're using it in the wrong place. In this case, you want the `update` property to be a regular function to have the correct `this` value. However, the call to `setTimeout` should use an arrow function to wrap a call to `this.update`. – kamoroso94 Aug 26 '17 at 03:53
  • @kamoroso94: You're right! This case *depends* on `this` being set by the caller. Don't know what I was thinking! – spanky Aug 26 '17 at 14:57