One of the concepts in JS that really perplexed me when I started learning about it years ago was the this keyword. When arrow functions were introduced in ES6, they brought with them a new relationship to the keyword this. I remember trying to educate myself on the different ways this could be affected by where functions were called or defined, and my brain would explode. I was overwhelmed by trying to understand the nuances, especially when it came to arrow functions.
However, at this point in my career, I have a much better understanding of this word means, and what the value of this will be when it is referenced.
In the words of Bruno Mars: ‘Don’t believe me? Just watch…’
The two most important things to my understanding of this were:
- This = the function invocation context. Invocation = calling.
- Functions which reference this are most likely methods (ie functions defined a property of an object)
- The value of this is determined by
- HOW the function is defined
- WHERE it is called/invoked
- Whether of not you are in strict mode
I think it can be a bit confusing that the value of this is called the invocation context, but it does not just defined by where it is invoked: it’s also affected by how its initialized or declared.
But lets not get ahead of ourselves. Lets start with a simple function declaration that just prints the value of this.
function printThis () {
console.log('this =', this);
}
printThis();
//this = window
If we call this function, we get the automatic value of window. A functional declaration has the implicit invocation context of the window.
However, if we define the function as a property of an object, we will get a different this value.
const someObject = {
testingThis: printThis()
};
someObject.printThis()
//this = someObject
So when we call printThis as a method of someObject, we get a this value of someObject!
But, suppose that we want this to be something specific! We want to ensure that the value of this refers to a particular context. We can perform explicit binding with .call( ) and .apply( ) functions.
For example, I want to ensure when I call printThis, the invocation context is my newObject.
const newObject = { }
someObject.printThis.call(newObject)
// this = newObject
Wow. You’re a wizard Harry!
When we explicitly bind a invocation context (this value) for a function, we override the implicit this value.
If we were not messing around and wanted to ensure that a function always referred to the same context, we would use the .bind( ) function. Its important to note that the .bind( ) function returns a new function with a permanent new this value.
const hardBoundPrintThis = printThis.bind(newObject)
hardBoundPrintThis()
// this = newObject
In strict mode, the value of this is undefined. You can call a function which references this to find out if you are strict mode…
function amIInStrictModeOhGod(){
return !this;
}
amIInStrictModeOhGod()
//if we are in strict mode, will return true
“But what about arrow functions Carly?!?!?!”: I hear you cry in a panic, “How does the value of this change???”
Calm thyself!
Arrow functions inherit this from the parent scope of where they are defined. They do not have their own binding to this. Because of this, they can make poor methods. Sometimes you might end up trying to reference a property on an object, but because you forgot that the arrow function does not have it’s own this, you will get an error:
const wowAnObject = {
fancyProperty: 'So fancy!',
anArrowFunction: ( ) => {
return this.fancyProperty
}
wowAnObject.anArrowFunction()
// error!
Trying to access fancyProperty will throw an error because the parent context of this method is the global window, which does not have a fancyProperty.
However, this aspect of arrow functions (inheritance of the invocation context) can be beneficial when generating classes…
class BestComponent extends React.Component {
constructor(props) {
super(props)
}
clickHandler = () => {
console.log('this =', this)
}
render() {
return <button onClick={this.clickHandler}>Wahoo!
</button>
}
}
Wherever this button is instantiated, because the clickHandler function was defined as an arrow function within the BestComponent class object, the lexical context of clickHandler will that of its parent, ie the BestComponent. You don’t have to use the .bind( ) method to ensure that it is always connected to BestComponent.
Thoughts!
- This is generally referenced within a method of an object
- This values can be implicitly, explicitly, or hard bound
- In general, it’s usually best to avoid utilizing this inside of arrow functions due to the potential complexities of the parent lexical scope.
Sources / References:
Flanagan D. Javascript: The Definitive Guide. 7th ed. Sebastopol: O’Reilly; 2020.
‘Understanding “this” in javascript with arrow functions’, Dario Garcia Moya, https://www.codementor.io/@dariogarciamoya/understanding-this-in-javascript-with-arrow-functions-gcpjwfyuc
‘Understanding “This” in JavaScript’, Dario Garcia Moya, https://www.codementor.io/@dariogarciamoya/understanding–this–in-javascript-du1084lyn