Analyzing the ‘This’ Keyword in es5 Functions vs. es6 Arrow Functions

As a new(ish) developer, a lot of concepts are overwhelming until I get ahold of my emotions and attempt to truly understand what is in front of me. This could be looking at a new error, digging through a coworker’s code, or learning a new concept such as arrow functions. The first time I saw arrow functions, I was like “what is this sorcery”, and dismissed it as something to learn at a later date. Little did I know how easy it would make my life as I took a dive into React. Part of being effective at using arrow functions is understanding how the keyword this differs between the two and how to effectively leverage this to write more effective code.

TL:DR- You should really read all the way through this. It's a very important concept.

How the es5 function is different from the arrow function (scoping, etc)

Es5 functions have some distinct differences from Arrow functions.

Let’s look at the following es5 function. You could use the .forEach() method or a for loop within the showStuff function, but I chose map (for no particular reason):

var dog = {
  name: 'rufus',
  age: 8,
  likes: ['running', 'jumping', 'barking'],
  showStuff: function() {
    this.likes.map(function(item) {
      console.log(this.name + 'likes ' + item)
      console.log(this);
    })
  }
}
dog.showStuff();


Here’s the console.log result:

likes running
likes jumping
likes barking


What happened to this.name? Let’s explore that. If you enter console.log(this) inside the function before the map statement, your console.log(this) will look like the following:



{name: "rufus", age: 8, likes: Array(3), showStuff: ƒ}
  age: 8
  likes: (3) ["running", "jumping", "barking"]
  name: "rufus"
  showStuff: ƒ ()


Now, move the console.log(this) inside the map statement (before or after the original console.log is fine). Notice the result of the console.log(this):



likes running
Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
likes jumping
Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
likes barking
Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}


Why did the scope for this change inside of the map statement? When this is inside of the object’s method, as seen here:

  showStuff: function() {
      console.log(this);
      this.likes.map(function(item) {
      console.log(this.name + 'likes to ' + item)
    })
  }

, the owner of the function is the object dog and this is bound to the dog object. When the console.log(this) is moved to the inside of the map statement, we see this changed to window. The inner map function now moves the scope away from the dog object to the parent object of dog, which is Window.

You may be saying “that’s obvious, but why does that happen?”. The answer is that it is by design of the Javascript language. Javascript is very loosely typed, which results in quirks such as this. While you can’t avoid this quirk, you can use a couple different methods to work around this binding in es5 syntax.

The first method involves declaring a variable in the method’s outer function, and setting the variable to this, as seen below:

  showStuff: function() {
    var correctBinding = this;
    this.likes.map(function(item) {
      console.log(correctBinding.name + ' likes ' + item)
      console.log(this);
    })
  }


You will see the result of correctBinding.name in the console.log change from blank to the following:



 rufus likes running
 -> Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
rufus likes jumping
-> Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
rufus likes barking
-> Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}


Pretty cool, huh? The next method involves using a .bind() method, which creates a new function that binds this to any arguments placed before it. An example can be seen here:



  showStuff: function() {
    this.likes.map(function(item) {
      console.log(this.name + 'likes ' + item)
      console.log(this);
    }.bind(this))
  }


This is the resulting console.log:

rufus likes running
-> {name: "rufus", age: 8, likes: Array(3), showStuff: ƒ}
rufus likes jumping
-> {name: "rufus", age: 8, likes: Array(3), showStuff: ƒ}
rufus likes barking
-> {name: "rufus", age: 8, likes: Array(3), showStuff: ƒ}

If you are noticing the same thing I am, you see that the console.log(this) is different between the two binding methods. The reason is because the .bind() method is binding the entire outer method function for showStuff. The variable declaration of correctBinding= this is only set when called, and is set on the inner function of the map statement, not the outer function of showStuff. If you would like to read more about the .bind() method, have a look here MDN Bind Reference


Phew!! That was intense. Ready to see an easier way to deal with this?


When es6 was released, it presented a wonderful new way to deal with the this keyword. It changed the this binding to a pattern called lexical scoping. The simplest way to describe lexical scoping is that the inner function (or any nested functions) have access to variables which are declared in it’s outer scope. In our examples, the outer function for the showStuff key in the dog object is holding the binding of this to the dog object. Now any nested functions (i.e. the map statement’s function) inherit the outer function’s this binding.

Here’s an example of the above code in es6:

const dog = {
  name: 'rufus',
  age: 8,
  likes: ['running', 'jumping', 'barking'],
  showStuff() {
    this.likes.map(item => {
      console.log(this.name + ' likes ' + item)
      console.log(this);
    })
  }
}
dog.showStuff();


Our results in the console are as follows:

rufus likes running
-> {name: "rufus", age: 8, likes: Array(3), showStuff: ƒ}
rufus likes jumping
-> {name: "rufus", age: 8, likes: Array(3), showStuff: ƒ}
rufus likes barking
-> {name: "rufus", age: 8, likes: Array(3), showStuff: ƒ}

The es6 function, a.k.a. “fat arrow function” has lexical scoping which binds the this of the inner function to dog and not Window. How cool is that? No worrying about Window anymore with this style of nested functions. It's really not more complicated than that, but takes a concentrated effort as a new developer to not get overwhelmed and fully grasp what is happening between the es5 and es6 code. If you would like to read more about lexical scoping, see the lexical scoping sections in this MDN closures reference: MDN Closures Reference