What is "this" doing inside an arrow function?

What is "this" doing inside an arrow function?

ยท

5 min read

Did you start using the arrow functions already? Observed any strange things or not an expected output? Do not blame javascript or the previous blog.
We need to understand the arrow functions behavior of this in an arrow function's scope, so one can decide when to use Also, few things that are missing in arrow function from the legacy function

Before we bind to the arrow functions let's understand this

"this" binding

In arrow functions, this is not that this we know from regular functions.๐Ÿ™„
Oops! If that was vague, the point here is - Arrow function does not have its own this in their scope, unlike regular functions.
So whenever you call this inside an arrow function, it refers to its parent's scope this binding

Really? ๐Ÿ˜ฎ How? See below code

const obj = { 
   i: 10,
   arrow: () => console.log(this.i, this), 
   normal: function() {
     console.log(this.i, this);
   }
}

obj.arrow();  
//this.i is undefined, as "this" will be pointing to global "this" (parent scope)
//because obj will not have "this" in its scope
obj.normal();   
//here i will be 10 because the regular function has its own "this", 
//which points to the current object i.e., "obj"

And below is the output

image.png

Hence, the arrow functions are best suited for non-method functions

But then, the above behavior has the below usecase

const obj = { 
   i: 10,

   method1: function() {
     console.log("method1 normal function ");
     console.log("i =",this.i,",this =",this);
     function innerFunc() {
          console.log("normal inner function");
          console.log("i =",this.i,",this =",this);
     }
    innerFunc();
   },
   method2: function() {
     console.log("method2 normal function");
     console.log("i =",this.i,",this =",this);
     const arrowInnerFunc = () => { 
           console.log("normal inner function");
           console.log("i =",this.i,",this =",this);
     }
    arrowInnerFunc();
   }
}

obj.method1();
obj.method2();

can you guess the output? ๐Ÿง

obj.method1();   
//method1 normal function    
//i = 10 ,this = {i: 10, method1: ฦ’, method2: ฦ’}   
//normal inner function  
//TypeError: Cannot read property 'i' of undefined  -> in strict mode  
or  
//i = undefined ,this = Window {0: global, window: Window, self: Window, document: document, name: "", location: Location, โ€ฆ} -> not in strict mode

wait, what?๐Ÿคฏ No, do not think we learned wrong. Understand here that
In method1, innerFunc is a normal function that will have its own this binding, and since we are not binding any value tothis

  • in strict mode, no longer possible to reference the window object through this inside a function, so TypeError
  • in sloppy mode, it is referring to window object

What happens in the case of arrowInnerFunc?๐Ÿค”
Keep thinking on this so at least you will get this answer correct ๐Ÿ˜ƒ

obj.method2();   
//method1 normal function    
//i = 10 ,this = {i: 10, method1: ฦ’, method2: ฦ’}   
//normal inner arrow function   
//i = 10 ,this = {i: 10, method1: ฦ’, method2: ฦ’}

High? ๐Ÿ˜… No, nothing new or wrong here
It is as simple as that arrowInnerFunc is an arrow function that will get the this binding from the parent scope where it has been called

It takes time to wrap our heads around this. Give it some time!

call, apply, and bind

Assuming the reader is aware of these methods and how they dependent on "this" in the scope, the below section will show the arrow functions behavior

Let's define an object and functions

const obj = {
    num: 10
};
window.num = 20; //defining num inside window object

const add = function (a) {
  return this.num + a ;
};

const addUsingArrow =  a => this.num + a ;
  • call
    const result1 = add.call(obj, 1) 
    console.log(result1) //11
    const arrowResult1 = addUsingArrow.call(obj, 1) 
    console.log(arrowResult1) //21 why?
    
  • apply
    const arr = [1, 2, 3]
    const result2 = add.apply(obj, arr) 
    console.log(result2) // 11
    const arrowResult2 = addUsingArrow.apply(obj, arr) 
    console.log(arrowResult2) // 21 why?
    
  • bind
    const result3 = add.bind(obj) 
    console.log(result3(1)) //11
    const arrowResult3 = addUsingArrow.bind(obj) 
    console.log(arrowResult3(1)) //21 why?
    

For all the above whys, the reason is again this binding in arrow functions will be pointing to its parent's this, and here it is global/window

prototype property

What if I say Arrow functions do not have a prototype property. Strange? but it's true!

const Foo = () => {};
console.log(Foo.prototype); // undefined

arguments

Not sure how often this being used and how many are aware of this.
Arrow functions do not have arguments binding. The below code will help us to understand the arguments keyword

const normalFunction = function () {
    console.log(arguments);
}
const arrowFunction = () => console.log("inside arrow",arguments)

normalFunction(1,2,3)
arrowFunction(1,2,3)  //error
//instead of arguments keyword, use spread operator

creating constructor

Arrow functions cannot be used as constructors and will throw an error when used with new. Again a limitation ๐Ÿคจ

// this is how we write regular constructor
function Person() {
    this.name = 'neoGrammer'
}
const person1 = new Person();

//constructor using arrow functions
const Person = () => {this.name = 'neoGrammer'};
const person1 = new Person(); // TypeError: Person is not a constructor

yield

Not going into detail about this, just a point to note that

Can not use yield, within its body

Closing notes

Yes, the blog pointed out the comparison of the arrow function to the legacy function and maybe sounded like disadvantages so not to use. But, the intention was to understand the intent and limitations, hence the right usage of the tools at the right place can be made

Hope, this blog clarified when to use and when to not use the arrow functions!

As always, open to hearing suggestions, improvisation...
Thanks for reading!

Resources

ย