Reputation: 481
I am confused regarding how to work with this
in combination with bind
. I know the problem stems from this
being the global object. Could somebody explain how I could circumvent?
In the constructor I have done this:
var BoundedStartTimer = this.StartTimer.bind(this);
var BoundedStopTimer = this.StopTimer.bind(this);
var BoundedClearTimeString = this.ClearTimeString.bind(this);
BoundedClearTimeString();
The last one works, but the timer does not start when I call BoundedStartTimer();
.
I am not sure what I am doing. Below are my declarations:
MemoryGame.prototype.StartTimer = function(){
var playTimeInMilliseconds = 0;
this.timeString = "";
this.timer = window.setInterval(function(){
if(playTimeInMilliseconds >= 1000)
this.timeString = .....
this.handleToTimerText.textContent = this.timeString;
}, 10);
}
MemoryGame.prototype.StopTimer = function(){
clearInterval(this.timer);
}
MemoryGame.prototype.ClearTimeString = function(){
this.handleToTimerText.textContent = "00:000";
}
Upvotes: 4
Views: 304
Reputation: 82267
The issue here is with your setInterval
call. setInterval
executes functions in the global scope, meaning that this === window
which is not ideal. You were right that bind
can work here though, just bind the interval callback to this
:
window.setInterval(function(){
if(playTimeInMilliseconds >= 1000)
this.timeString = .....
this.handleToTimerText.textContent = this.timeString;
}.bind(this), 10);
// ^ bind this from your MemoryGame instance to the interval callback
Upvotes: 3
Reputation: 9807
So here's the deal.
Every time you see the keyword function
, you should think two things.
First is, "oh, this creates a new scope for variables!" Lots of people from Java think this happens with each curly brace {
that is not used as an object literal; that's false, it's only a function
context (or, now with ES6, there is let
and =>
that do it too.)
The second thing you should think is, "oh, probably this
and arguments
are going to be different in the new variable scope." That's because these two keywords are especially "magical" in JavaScript.
So when you write:
this.timer = window.setInterval(function () {
if (playTimeInMilliseconds >= 1000) {
this.timeString = .....
}
this.handleToTimerText.textContent = this.timeString;
}, 10);
...you have to see that function
as a new variable scope with its own this
and arguments
.
Now, playTimeInMilliseconds
is going to be fine if you don't at any point in this function write var playTimeInMilliseconds
, which will get "hoisted" to the top of the function declaration and declare a new variable local to that scope. As long as you never do this, playTimeInMilliseconds
will peek at the parent variable scope and find the variable you defined in the outer scope.
However, this.timeString
and this.handleToTimerText
are not fine because the value of this
was declared to be window
.
There are two remedial approaches:
In the outer function, capture this
into your own variable. Common variable names for this purpose are are me
and self
. Just write var self = this;
in the outer function, then do not write var self
in the inner function, then write self.timeString
and self.handleToTimerText
.
Take the function and explicitly .bind()
it. So pull out the function like so:
function updateTimerText() {
if (playTimeInMilliseconds >= 1000) {
this.timeString = .....
}
this.handleToTimerText.textContent = this.timeString;
}
this.timer = setInterval(updateTimerText.bind(this), 10);
Obviously, bind
has a lot of power! So don't do crap like this:
var BoundedStartTimer = this.StartTimer.bind(this);
var BoundedStopTimer = this.StopTimer.bind(this);
var BoundedClearTimeString = this.ClearTimeString.bind(this);
BoundedClearTimeString();
I have no idea what scope this stuff is occurring in, but it's probably not in the right scope. Because whatever you're binding as this
has to be the instance of new MemoryGame()
that you created -- maybe let's say you called it var myMemoryGame = new MemoryGame();
or so; just use myMemoryGame.startTimer()
(or whatever) and let the this
come naturally. Whenever you write a.b.c.d.e()
the this
in the context of function e()
is magically set to a.b.c.d
. Don't reassign it to this
unless you know what the heck you're doing and that you have to preserve this
in some new context. At the very least be nice to readers and .bind(myMemoryGame)
so that it's 100% clear.
Upvotes: 2