36

I have a setTimeout defined inside of a function that controls the player's respawn (i am creating a game):

var player = {
    ...
    death:(function() {
        this.alive = false;
        Console.log("death!");
        var timer3 = setTimeout((function() {
            this.alive = true;
            Console.log("alive!");
        }),3000);
    }),
    ...
}

When it executes, I read in the console, "death!" and 3 seconds later "alive!". However, alive is never really set back to true, because if i write player.alive in the console, it returns false. How come i can see "alive!" but the variable is never set back to true?

1
  • in this scope this is the anonymous function passed to the setTimeout, you need to assign this to some other variable and check it later Commented Jul 30, 2012 at 1:21

5 Answers 5

50

You have to be careful with this. You need to assign your this in the outer scope to a variable. The this keyword always refers to the this of the current scope, which changes any time you wrap something in function() { ... }.

var thing = this;
thing.alive = false;
Console.log("death!");
var timer3 = setTimeout((function() {
    thing.alive = true;
    Console.log("alive!");
}),3000);

This should give you better success.

Update 2019-10-09: The original answer is true, but another option is now available for recent versions of JavaScript. Instead of using function, you can use an arrow function instead, which does not modify this:

this.alive = false;
Console.log("death!");
var timer3 = setTimeout(() => {
    this.alive = true;
    Console.log("alive!");
}), 3000);

This is supported from ES6 forward, which is part of all current browsers but IE (of course), I think. If you are using a modern framework to build your project via Babel or whatever, the framework should make sure this works as expected everywhere.

Sign up to request clarification or add additional context in comments.

3 Comments

For extra credit, read up on javascript's function .call() and .apply() methods, which help to understand how this works in javascript, and can come in handy when you need to define the function somewhere other than where you're passing it. jQuery's .proxy() and Prototype's .bind() provide neat wrappers to this functionality.
Further to what @BrianMortenson said, JS has its own .bind() method (introduced in version 1.8.5, so not supported in older IE). Actually one of the examples on that MDN page is for use with setTimeout().
Further to what @nnnnnn said, if you are using ES6 you can use arrowed functions. () => setTimeout( ()=> this.alive = true, 3000)
25

It's because this in the setTimeout handler is referring to window, which is presumably not the same value as referenced by this outside the handler.

You can cache the outer value, and use it inside...

var self = this;

var timer3 = setTimeout((function() {
    self.alive = true;
    Console.log("alive!");
}),3000);

...or you can use ES5 Function.prototype.bind...

var timer3 = setTimeout((function() {
    this.alive = true;
    Console.log("alive!");
}.bind(this)),3000);

...though if you're supporting legacy implementations, you'll need to add a shim to Function.prototype.


...or if you're working in an ES6 environment...

var timer3 = setTimeout(()=>{
    this.alive = true;
    Console.log("alive!");
},3000);

Because there's no binding of this in Arrow functions.

Comments

10

Just in case anyone reads this, the new javascript syntax allows you to bind a scope to a function with "bind":

window.setTimeout(this.doSomething.bind(this), 1000);

1 Comment

Unfortunately binding directly to the function like this doesn't appear to work (at least, not in Chrome 60). It seems that you have to run it through an anonymous function first, as in the "community wiki" ES5 example above.
5

Probably because this isn't preserved in the timeout callback. Try:

var that = this;
...
var timer3 = setTimeout(function() {
    that.alive = true;
    ...

Update (2017) - or use a lambda function, which will implicitly capture this:

var timer3 = setTimeout(() => {
    this.alive = true;
    ...

2 Comments

In my humble opinion the that = this solution should not be used anymore because it only adds to the confusion caused by the this keyword. Preferably you should use arrow notation setInterval( () => {}) or else use bind() to fix javascript's scoping issues.
@Kokodoko I agree! This was answered in 2012. I'll update the answer.
5

With ES6 function syntax, the scope for 'this' doesn't change inside setTimeout:

var timer3 = setTimeout((() => {
    this.alive = true;
    console.log("alive!");
}), 3000);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.