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.