Hi there everyone! I hope you are doing good, I am back with another article, and this time it is an unconventional topic -- When not to use arrow functions?
Pre-requisites
- The reader should have a basic knowledge of what is ES6 arrow functions, a basic syntax level understanding is more than enough.
- Understanding the
this
keyword in JS is a plus. Please check my blog if not seen already The "this" keyword in JS uWu.
Let's start
If you are continuing you have a basic knowledge of ES6 arrow functions and the one thing that almost everyone loves about it is actually a very cool way to write functions, the syntax is very easy, and it looks very clean. Before starting when not to use the arrow functions let me give you a basic thumb rule if the function you are writing/converting doesn't have a this
inside it, please convert it into the arrow function, go nuts!
But, If your function includes this
which is very common in JS, as it is the basis of logic sharing in JS, you need to follow some gotchas, before the gotchas, let's just get the one basic right about arrow functions.
Most important basic about arrow function
Arrow function doesn't create its own context i.e
this
, it just uses the context, of where it was created, i.e surrounding scope.
A way to resolve the value of this
inside an arrow function, is to check the value of this
just before the function is being created, the value of this
just before the creation of function, is the value of this
inside the function.
Without further ado, let's dive deep into the gotchas --
Object Methods
Consider the following snippet of code
const me = {
name:"Mihir",
talk:()=> {
return this;
}
}
me.talk();
The output of this would be the global object, as I am running the code in the browser and not in the strict mode
, this will be the window
object. In the same code, if we write the talk()
as normal traditional functions, we would get the me
object over here. So why did this happen? The same reason I stated above Arrow function doesn't create their own context, they just use the context of the surrounding scope at the time of function creation.
Woohoo! Now we get the reason, but people will say let's debug/resolve the value of this
inside the function in the way which I said above, and they will write this particular code.
const me = {
name:"Mihir",
talk:this
}
me.talk;
Over here you will see it is the window
object which tells us the story-- ok whatever was the value of this
outside the function creation, is the same inside it, but people would start pulling their hair but why on earth the value of this
inside the object me
is the global object, i.e the window
object, the person who doesn't understand this
in JS, will not be able to understand the concept. There is this one rule regarding this
--
Objects don't create a binding with
this
, functions do.
and that my friend is the reason why JS is hated by so many people. If you are coming from an OOP background, you will immediately start hating JS and say why on earth this
is not equal to object me
, but this is what it is.
I have taken this example in my previous blog The "this" keyword in JS, I will take that example again and we will try to resolve the value of this
in that example again, with the set of rules I have given above.
const me = {
fName:"Mihir",
// Tradional function only, you can write tradional function inside a obj like this also
talk() {
setTimeout(function () {
console.log(this.fName)
},0)
}
}
me.talk()
This code above will result in undefined
, why? Because the callback function inside setTimeout
is actually executed in a different scope and the value of this
depends on how is the function executed, where is the function exected, who executes it. I will have a blog regarding this later, but for the time being, just understand it executes outside in a different scope, and therefore this
is undefined.
Now you use the arrow function for the callback function inside setTimeout
and boom! It starts working.
const me = {
fName:"Mihir",
// Tradional function only, you can write tradional function inside a obj like this also
talk() {
setTimeout(()=> {
console.log(this.fName)
},0)
}
}
me.talk()
To check this thing we will use our golden rules, just before the callback function we will check the value of this
.
const me = {
fName:"Mihir",
// Tradional function only, you can write tradional function inside a obj like this also
talk() {
console.log(this)
setTimeout(()=> {
console.log(this.fName)
},0)
}
}
me.talk()
The value of this
outside the function, is the value inside the callback function.
Callback functions are generally the functions where you should be using arrow functions, but there is one catch, I will tell that later.
Do not use arrow functions with prototypes
function Person(n){
this.fName=n;
}
Person.prototype.talk=function() {
console.log(`${this.fName} started talking`)
}
Person.prototype.arrowTalk=()=> {
console.log(`${this.fName} started talking`)
}
const me=new Person("Mihir")
me.talk() // Works
me.arrowTalk() // Doesn't work
Why this is not working because inarrowTalk()
this
of surrounding scope is taken, at the time of function creation in that scope the value of this
is the window
object.
Do not use the arrow functions to create Constructor Functions.
const Person=(n)=>{
this.fName=n;
}
const me = new Person("Mihir") // Throws error
It just throws an error, no questions asked.
Do not use the arrow functions in event handlers
Wait, wait! I know people will start screaming at me because above I mentioned it is a good practice to use arrow functions inside callback functions, and event handlers are one of them. So why? This is because of how event listeners work under the hood.
document.body.addEventListener("click", function() {
console.log(this) // You get the body element
})
This is because the event listener under the hood binds the this
to the element on which the event listener was registered. This is done by the event listener on its own. If you have used jQuery
you might be getting some flashbacks, it's ok!
Now convert the callback function into the arrow function.
document.body.addEventListener("click", ()=> {
console.log(this) // window object
})
Why does this happen? Because this is just an arrow function and it derives the this
value from the surrounding scope when the function is declared, but all the cool devs out there won't find this as a problem, because they are cool and they would do something like this to get the element --
document.body.addEventListener("click", (e)=> {
console.log(e.target)
})
and then it fails! why? This returns the target element, not the body
element, the target element is the element from which it was triggered, body
element is the element on which this particular event listener was registered. You can run the above code on any website, and click on the website at different places, you will get different elements every time.
Which is more verbose as well as declarative according to me, but those are just my thoughts.
So in which callbacks do you use the arrow function and on which one shouldn't. Basically, it depends on how the function works under the hood, to know more check out this blog "the saga of arrow functions with callback functions"
Summary
So don't use the arrow functions in --
- Object Methods
- With prototypes
- Creation of Constructor functions
- Event Handlers or such type of functions, refer blog
Till then peace! If you like my work please share! Thanks! Have a nice day!