0

Apologies for the newbie question, but consider this code

function Greeter( name) { this.name = name; }

Greeter.prototype.delayed_greet = function() { 
  setTimeout( function cb() { 
    console.log(' Hello ' + this.name); 
  }, 500); 
};

Greeter.prototype.greet = function() { 
    console.log(' Hello ' + this.name); 
}

const greeter = new Greeter(' World');

greeter.delayed_greet(); // will print "Hello undefined"
greeter.greet(); // will print "Hello World"

So in the delayed_greet method, what does this refer to when it's nested inside the setTimeout? It's obviously not referring to the greeter object otherwise it would work.

User314159
  • 7,733
  • 9
  • 39
  • 63

1 Answers1

3

setTimeout is generally defined as window.setTimeout in browsers, and can be called as just setTimeout because it's available in the global scope.

That also means the context, and this value, is always window, unless another this value is explicitly set.

MDN says

Code executed by setTimeout() is called from an execution context separate from the function from which setTimeout was called.

The usual rules for setting the this keyword for the called function apply, and if you have not set this in the call or with bind, it will default to the global (or window) object in non–strict mode, or be undefined in strict mode.

It will not be the same as the this value for the function that called setTimeout.

MDN also outlines a number of ways to solve the "this-problem" in setTimeout. Personally I think I would just take the easy way out, and use a variable

Greeter.prototype.delayed_greet = function() { 
   var that = this;

   setTimeout( function cb() { 
       console.log(' Hello ' + that.name); 
   }, 500); 
};

Another option would be an arrow function, as they keep the surrounding context and don't create their own context.

var o = { 
    fn () { 
        setTimeout( () => { console.log(this) }, 500) 
    } 
}

var o2 = { 
    fn () { 
        setTimeout( function() { 
            console.log(this === window) 
        }, 1000) 
    } 
}

o.fn();  // fn()              ---  arrow function
o2.fn(); // true, is window   ---  regular function
adeneo
  • 312,895
  • 29
  • 395
  • 388
  • 2
    If ES6, [a fat arrow would make sense too](https://jsfiddle.net/52ofc0ca/) - one less variable. (edit: I see that was covered in the MDN documentation). – Andy Sep 09 '17 at 21:03
  • Yes, arrow functons are "special", they keep the surrounding context, even in timers -> https://jsfiddle.net/j5qsvs9t/2/ – adeneo Sep 09 '17 at 21:08
  • Is there a specific reason you didn't add a `bind` example ? – doubleOrt Sep 24 '17 at 23:17
  • 1
    @Taurus - no, not really, but that's an option as well. All modern browsers also supports additional arguments being passed to `setTimeout`, so one can do `setTimeout( (that) => {}, 1000, this )` as well to pass in some value etc. – adeneo Sep 24 '17 at 23:30
  • @adeneo I just asked because `bind` does get omitted a lot in questions it could be applied to positively (you did mention `bind`, but i wondered why there wasn't an example. Thanks for the extra parameter reminder :) had forgotten about them, but is browser-support for these context parameters really that great ? – doubleOrt Sep 24 '17 at 23:45
  • @adeneo Ahaa, now i get what you meant. – doubleOrt Sep 26 '17 at 04:36
  • @adeneo You could achieve the same result with `var x = (that) => {console.log(that);}; setTimeout(function() {x(20);} , 1000);`, right ? – doubleOrt Sep 26 '17 at 04:39