Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is an anonymous function required to preserve "this" using setTimeout

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"
};
like image 390
Ben Foster Avatar asked Jan 29 '14 11:01

Ben Foster


People also ask

What is the purpose of anonymous functions?

Anonymous functions, also known as closures , allow the creation of functions which have no specified name. They are most useful as the value of callable parameters, but they have many other uses. Anonymous functions are implemented using the Closure class.

Why do we need anonymous function in JavaScript?

An anonymous function is not accessible after its initial creation, it can only be accessed by a variable it is stored in as a function as a value. 3. This function is useful for all scenarios. An anonymous function can be useful for creating IIFE(Immediately Invoked Function Expression).

What is anonymous function with example?

An anonymous function is a function that was declared without any named identifier to refer to it. As such, an anonymous function is usually not accessible after its initial creation. Normal function definition: function hello() { alert('Hello world'); } hello();

Can you pass anonymous function as an argument to another function?

Summary. Anonymous functions are functions without names. Anonymous functions can be used as an argument to other functions or as an immediately invoked function execution.


2 Answers

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?

like image 74
Qantas 94 Heavy Avatar answered Oct 22 '22 11:10

Qantas 94 Heavy


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
   });
}
like image 2
Matyas Avatar answered Oct 22 '22 12:10

Matyas