This in JavaScript
Are you confused by the this
keyword in JavaScript? It confuses everyone in the beginning, so don’t worry about it. You’re not alone.
But that doesn’t mean you can go on without understanding this
forever. It is used so much in JavaScript and in tutorials everywhere that you need to grasp what this
is sooner or later. Once you understand this
, you’ll realize that it’s much simpler than you think it is.
By the end of this article, you would have demystified this
for yourself. You’ll know what it is, what it does and how to use it.
So, what is this?
this
is a keyword whose value changes depending on how a function gets called. There six different ways where this
can take on new values. They are:
this
in global contextthis
in object constructionthis
in an object methodthis
in a simple functionthis
in an arrow functionthis
in an event listener
You may wonder what this
is in each context and why there’s a need to change this
in the first place. To answer your question, let look at how this
changes in each of the six contexts.
This in a global context
When this
is called outside of any function, in a global context, this defaults to the Window
object in the browser.
console.log(this) // Window
Usually, you wouldn’t use this
in a global context anyway, so the value of this
here doesn’t really matter. Let’s move on to the next context.
This in object construction
When you create a new instance of an object with the new
keyword, this
refers to the instance.
function Person(age) {
this.age = age
}
const greg = new Person(22)
const thomas = new Person(24)
console.log(greg) // this.age = 22
console.log(thomas) // this.age = 24
You can see that greg
is an instance of Person
in the code above. Now, whenever you reference greg
, you won’t accidentally get thomas
. So, setting this
to be the instance makes perfect sense.
Let’s look at a closely related context next – this
in an object method.
This in an object method
Methods are fancy words for functions that are associated with an object, like this:
(Note: Methods here are defined with the ES6 object literal shorthand. Check out this article if you’re unsure what it does).
let o = {
// A method
aMethod() {},
}
this
within any method refers to the object itself.
let o = {
sayThis() {
console.log(this)
},
}
o.sayThis() // o
Since this
refers to the object, you can use methods to get the instance of an object, like this:
function Human(name) {
return {
name,
getName() {
return this.name
},
}
}
const zell = new Human('Zell')
const vincy = new Human('Vincy')
console.log(zell.getName()) // Zell
In these two object contexts, you can see that the changed value of this
lets you get the right instance, which is the basis for Object-oriented programming. That’s a topic for another day though.
Let’s move on to the next context.
This in a simple function
Simple functions are functions you know extremely well; like the one below. Anonymous functions written in the same form are also considered simple functions.
function hello() {
// say hello!
}
On browsers, this
is always set to Window
in a simple function. The same is true even if you call a simple function in an object method.
function simpleFunction() {
console.log(this)
}
const o = {
sayThis() {
simpleFunction()
},
}
simpleFunction() // Window
o.sayThis() // Window
Unfortunately, the change in this
is unexpected for beginners. They expect this
to remain the same within object methods. I got caught in it too.
To see why, consider the following code. Here, a this.speakLeet
function is executed later within a setTimeout
function.
const o = {
doSomethingLater() {
setTimeout(function () {
this.speakLeet() // Error
}, 1000)
},
speakLeet() {
console.log(`1337 15 4W350M3`)
},
}
Unfortunately, the code above results in an error. The error occurs because this
is set to Window
in the setTimeout
function. Window
does not have a speakLeet
method.
One quick fix is to create a variable that stores the reference to the this
. This variable is often called self
or that
.
const o = {
doSomethingLater() {
const self = this
setTimeout(function () {
self.speakLeet()
}, 1000)
},
speakLeet() {
console.log(`1337 15 4W350M3`)
},
}
A second way to fix this problem is to use the new ES6 arrow functions, which brings us to the next context.
This in arrow functions
this
in an arrow function is always the same as this
around it (in its immediate scope). So, if you use arrow functions within an object method, the this
context stays as the object, not Window
.
With arrow functions, the speakLeet
example could be written in the following way:
const o = {
doSomethingLater() {
setTimeout(() => this.speakLeet(), 1000)
},
speakLeet() {
console.log(`1337 15 4W350M3`)
},
}
(For more info on arrow functions, read this article)
A third way to change the value of this
within any function is to use either bind
, call
or apply
. We’ll look at bind
later in the article, and call
and apply
another time. But first, let’s go through the final context — event listeners.
This in event listeners
this
is set to the element that fired the event in an event listener:
let button = document.querySelector('button')
button.addEventListener('click', function () {
console.log(this) // button
})
When creating more complex components, you may find yourself creating event listeners within methods.
function LeetSpeaker(elem) {
return {
listenClick() {
elem.addEventListener('click', function () {
// Do something here
})
},
}
}
Since this
refers to the element in the event listener, if you need to activate another method, you need to provide a reference to the object with.
function LeetSpeaker(elem) {
return {
listenClick() {
const self = this
elem.addEventListener('click', function () {
self.speakLeet()
})
},
speakLeet() {
console.log(`1337 15 4W350M3`)
},
}
}
Alternatively, you can use an arrow function. If you do so, you can still get the element with event.currentTarget
.
function LeetSpeaker(elem) {
return {
listenClick() {
elem.addEventListener('click', e => {
console.log(e.currentTarget) // elem
this.speakLeet()
})
},
speakLeet() {
console.log(`1337 15 4W350M3`)
},
}
}
But both methods aren’t good enough to help you remove event listeners when the need arises, both are anonymous functions.
To remove an event listener, the callback passed as the second value needs to be a named function:
function someFunction() {
console.log('do something')
// Removes the event listener.
document.removeEventListener('click', someFunction)
}
document.addEventListener('click', someFunction)
(Here’s more on callbacks if you need help with them).
If you need this
to reference the object in an event listener, you need to use bind
to manually create a this
context.
function LeetSpeaker(elem) {
return {
listenClick() {
this.listener = this.speakLeet.bind(this)
elem.addEventListener('click', this.listener)
},
speakLeet(e) {
const elem = e.currentTarget
console.log(`1337 15 4W350M3`)
elem.removeEventListener('click', this.listener)
},
}
}
The code above may confuse you if you’ve don’t understand bind
. So, before I show you what is happening, let’s take a detour and understand what bind
does.
Changing this with bind
bind
is a method that is present in every function. It allows you to change the this
context. This method takes in any number of arguments and returns the bound function.
function sayThis() {
console.log(this)
}
const boundFunc = sayThis.bind(/* arguments...*/)
The first parameter you pass into bind
becomes this
in the bound function. Once you have created a bound function, you can call it anytime you wish:
function sayThis() {
console.log(this)
}
const boundFunc = sayThis.bind({ hippy: 'hipster' })
boundFunc()
The other parameters you pass to bind
will be passed as arguments to the original function
function sayParams(...args) {
console.log(...args)
}
const boundFunc = sayParams.bind(null, 1, 2, 3, 4, 5)
boundFunc()
Note: bind
doesn’t work with arrow functions.
That’s really all you need to know about bind
.
Now, let’s look back at the code to remove event listeners and dissect what is happening:
function LeetSpeaker(elem) {
return {
listenClick() {
// Binds this.speakLeet with a reference to the instance.
// Sets bound function to this.listener, so we can remove it later.
this.listener = this.speakLeet.bind(this)
elem.addEventListener('click', this.listener)
},
speakLeet(e) {
console.log(`1337 15 4W350M3`)
// Gets the element so we can remove the event listener.
const elem = e.currentTarget
// Removes the event listener.
elem.removeEventListener('click', this.listener)
},
}
}
Here’s a Codepen for you to see the code in action.
See the Pen SpeakLeetOnce? by Zell Liew (@zellwk) on CodePen.
And that’s all you need to know about this
.
Let’s wrap up.
Wrapping up
this
is a crucial keyword in JavaScript. It appears in many JavaScript frameworks, so you have to know what it does.
In this article, you learned about the six different contexts where this
takes on different values. You also learned how to change the this
context with functions like bind
. Additionally, you also learned to remove event listeners properly.
That’s all you need to know about this. Just master the concepts taught in this article and you won’t ever get confused anymore.
Before we end off the article, you may have heard people suggest you don’t use this
because it’s confusing. I implore you to you to consider reading this article. It might change your view of learning JavaScript.
If you have any questions, just hit me up in the comments below :)