JavaScript — a primer on “this”

JavaScript’s this keyword can be quite tricky to comprehend. How many times have you found yourself asking, “Is this this, or is this that?” this can get very complicated very quickly, so I thought I’d write a simple, short primer to ease you into the topic, using an example of a common misunderstanding.

What is this?

this is used as a reference to an object; specifically, it refers to the object that invokes the function where this is used. It may help to think of the object to which this refers as the owner of the function. For example:

var timer = {
seconds: 3,
status: function(){
return this.seconds + " seconds remaining.";
}
}
timer.status(); // "3 seconds remaining." 

We have defined a timer object, which has a seconds property and a status method. Here, this refers to our timer object since our timer object invokes the status method.

A common problem

So, this seems simple enough. What’s the big deal? Sometimes, it appears that this will refer to the object where it’s defined, rather than where it’s invoked. Let’s add a countDown method to our timer object to illustrate:

var timer = {
seconds: 3,
status: function(){
console.log(this.seconds + " seconds remaining.")
},
countDown: function(){
setInterval(function(){
console.log(this.status())
this.seconds--
}, 1000)
}
}
timer.countDown() // this.status is not a function

What’s going on? this appears to be defined within our timer object, but we get an error that “this.status is not a function.” If we look closer, we can see that the callback (which is an object itself) in our setInterval function is actually the owner of this, so the value of this is set to its default value, which is the global window object.

The solution

So, how do we solve this? ES6, ES2015, whatever you want to call it, introduced “arrow functions,” which allow us to lexically bind the value of this. All that means is that with arrow functions, the value of this is static and determined by the scope in which it is defined. Compare that to what we’ve discussed before, which is that the value of this is dynamic and determined by the scope in which it is invoked. So, now, let’s try this out:

var timer = {
seconds: 3,
status: function(){
console.log(this.seconds + " seconds remaining.")
},
countDown: function(){
setInterval(() => {
console.log(this.status())
this.seconds--
}, 1000)
}
}
timer.countDown() // 3 seconds remaining.

Woohoo! It works!

A note on object methods

According to MDN, arrow functions are best suited for non-method functions. Thus, in our timer function, we could not lexically bind the value of this:

var timer = {
seconds: 3,
status: function(){
console.log(this.seconds + " seconds remaining.")
},
countDown: function(){
setInterval(() => {
console.log(this.status())
this.seconds--
}, 1000)
},
logSeconds: () => {
console.log(this.seconds)
}
}
timer.logSeconds() // undefined 

As I mentioned, there is a lot more complexity to this than explained here. For a more in depth look, check out some of these resources: