8

I've used setTimeout plenty of times passing a function as a reference e.g.

setTimeout(someFunction, 3000);

In some cases, to preserve the value of this I've had to assign it to a variable before hand, but don't understand why the following does not work:

var logger = {
    log: function() { 
        var that = this;
        console.log(that.msg); 
        setTimeout(that.log, 3000); 
    },
    msg: "test"
};

logger.log();

Using an anonymous function however, does work:

var logger = {
    log: function() { 
        var that = this;
        console.log(that.msg); 
        setTimeout(function() { that.log() }, 3000); 
    },
    msg: "test"
};
Ben Foster
  • 34,340
  • 40
  • 176
  • 285
  • 6
    [The "this" problem](https://developer.mozilla.org/en-US/docs/Web/API/Window.setTimeout#The_.22this.22_problem): _Code executed by setTimeout() is run in a separate execution context to the function from which it was called. As a consequence, the this keyword for the called function will be set to the window (or global) object; it will not be the same as the this value for the function that called setTimeout._ – Andreas Jan 29 '14 at 11:12
  • Not sure if it's an exact dupe, but seems to be similar: http://stackoverflow.com/questions/1101668/how-to-use-settimeout-to-invoke-object-itself – Qantas 94 Heavy Jan 29 '14 at 11:25

2 Answers2

5

This doesn't work as setTimeout calls a function with the this value as the global object, not the parent object. You're passing a value into the setTimeout function -- it doesn't know how it's been accessed, and therefore cannot call it with the correct this value (unlike normal variables, the value of this is only determined when you call the function, unless this has been bound to a specific value using Function.prototype.bind).

By changing that to an anonymous function, you're using the closure to access the value of that, even when called as a value (the variable scope of a function is set when it is defined, not when it is run).

It's just like if you do something like this:

var a = { b: function () { return this.foo; }, foo: 'proper' };
function test(arg) {
    return arg();
}
var foo = 'random';
console.log(a.b()); // proper
console.log(test(a.b)); // random

There's also a related question on using this with setTimeout: Pass correct "this" context to setTimeout callback?

Community
  • 1
  • 1
Qantas 94 Heavy
  • 15,750
  • 31
  • 68
  • 83
2

Because in the first case you reference only the function log that is within the that object, but its relationship to that is lost. Think of it as setTimeout calls directly the log method at the stored memory address with the global context.

In the second example however you come from a global context, but first that is looked up, and afterwards log which is called then with the context of that.

Think of setTimeout having the following structure:

var setTimeout = function (func, time) {
   someWaitMechanism(time, function () { //this is called after the timeout
       func.call(null); //calls func with global scope
   });
}
Matyas
  • 13,473
  • 3
  • 60
  • 73