The meaning of "this"?

Game.prototype.restart = function () {
  this.clearLocalStorage();
  this.timer = setTimeout(function() {
    this.clearBoard();    // what is "this"?
  }, 0);
};

Executing the above code results in the following error:
Uncaught TypeError: undefined is not a function
Why?

At what line is the error happening? Is it in this.clearBoard()?

:grinning:

The problem is that this in functions is defined dynamically. That means that what determines the value of this is how the function is called. If a function is called by itself and not as a method of another object, it uses the default this binding which will give you the global object (window in browsers) or undefined if running in strict mode

// in browser
myFunction() // this = window 

If called from an object, a function is seen as a method and uses that object as its this

someObj.myFunction() // this = someObj 

When you call functions from an existing this, that same this is used in the function because that’s the object its called from

this.myFunction() // this = this 

When working with classes this is normally how you maintain the correct this throughout your method calls from object instances.

function Game () {}
Game.prototype.restart = function () {
  this.clearLocalStorage();
}
Game.prototype.clearLocalStorage = function () {
  // do stuff
}

var game = new Game();
game.restart(); // this = game
// in restart: this.clearLocalStorage() // this = game
// in clearLocalStorage: // this = game...

When you have a function that is not called from an object, for example your instance or this it loses that association and gets the default this binding.

Game.prototype.restart = function () {
  this.clearLocalStorage();
  var temp = function() {
    this.clearBoard(); // this = window (see below)
  };
  temp(); // not called from object, this = window
};

And this is the same thing that happens when you pass an inline function into setTimeout. You’re just passing a function and when it gets called internally by setTimeout, it doesn’t get called from an object such as your game instance, it gets called just like temp is called above

Game.prototype.restart = function () {
  this.clearLocalStorage(); // this = game instance
  this.timer = setTimeout(function() { // not called from this
    this.clearBoard(); // this = window
  }, 0);
};

To fix this, you can use arrow functions. Arrow functions don’t follow the same rules for this binding. Instead, they always pull the value of this from the parent scope.

Game.prototype.restart = function () {
  this.clearLocalStorage(); // this = game instance
  this.timer = setTimeout(() => { // uses value of this above ^
    this.clearBoard(); // this = game instance
  }, 0);
};

If sticking to the older ES5 syntax, you can use bind() to force your function to always be called with a this value of your choosing, no matter how its called.

Game.prototype.restart = function () {
  this.clearLocalStorage(); // this = game instance
  this.timer = setTimeout(function() { // this = game instance thanks to bind
    this.clearBoard(); // this = game instance
  }.bind(this), 0); // bind forces function this = this (game instance)
};

In both cases, given that the timeout function is not being called off the instance, and instead being called like temp(), using either arrow functions or bind() lets us override that dynamic this behavior and force the function to be called with a specific this value.

3 Likes