简体   繁体   中英

Why is an anonymous function required to preserve “this” using setTimeout

I've used setTimeout plenty of times passing a function as a reference eg

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"
};

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?

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM