Benefit of using arrow functions for methods

In the last section, functions, we touched on arrow functions and how to use them to really reduce the amount of boilerplate in our code, but we left out one of the major reasons that the arrow function was created to solve. We're going to dive into that in this video.

Let's say we're building a social media app where users can meet people around them. Right here we have an example with a basic user object that consists of a couple of properties, username and title. For each user, we want to display their data across the app to other users on their profile page. So that's why we have a method on userData to get their bio. But the

Now we're going to talk about a very important concept in JavaScript that confuses a lot of developers--the this keyword.

We're going to provide a proper understand of the this keyword in the right context so that you'll always be aware of how it should be used--in what context. So pay attention--the this keyword is a feature of functions, not anything else, that allows us to get access to an object's data.

const userData = {
  username: "Reed",
  title: "JavaScript Programmer",
  getBio() {
    console.log(`User (username) is a (title)`);
  },
};

However, as you might have already encountered if you've been programming with JS before is that the this keyword is a tricky concept to fully grasp. There's probably no concept that's been discussed or broken down as the this keyword. And that's because the this keyword's value is determined dynamically, based on how the function is called that references it.

But don't worry to much about this right now. We're going to do a very deep dive into the this keyword in our essential concepts video that's going to make you confident in always knowing what the this keyword is set to at any point in time.

But returning to our example, and to our definition of the this keyword, we can use the this keyword within our function here to be able to access the two properties we need to make this function work. You can think of this as being equal to 'userData'. So that allows us to say:

const userData = {
  username: "Reed",
  title: "JavaScript Programmer",
  getBio() {
    console.log(`User ${this.username} is a ${this.title}`);
  },
};

And then we can call this method by saying userData.getBio

[Run code] And we get that user Reed is a JavaScript programmer.

The convenience of using the this keyword is that we don't have to look at the variable name that the object's been assigned to. If we did use that to reference the object properties, we would have to keep changing the reference constantly if the variable it was stored in were to change. So many developers don't realize it, but the this keyword relieves us from a lot of tedious work.

I want you to take a mental snapshot of this example here and be aware that this is the primary use of the this keyword. It was provided to the language for methods on objects to be able to use properties off of the objects that they were on.

So before we move on, I want you to be aware of the fact that this method is a function made with a function declaration. And only functions declared with a function keyword have a dynamic this, meaning the value of this is determined by how it is called. This will be important for what's going to happen next.

const userData = {
  username: "Reed",
  title: "JavaScript Programmer",
  getBio() {
    console.log(`User ${this.username} is a ${this.title}`);
  },
};

Now let's say that in building this app, we want, if one user is looking at another user's profile for a while, we ask them if they want to friend that particular user. So in the real world, we might do that after about 5 to 10 seconds, but to test this feature out, we'll ask them after 2 seconds.

As a result, we create a method maybe called askToFriend, and this will use a function called setTimeout to ask our user to friend another after a certain period of time. And setTimeout is a higher order function, so it will need to take our console.log within its own function that it accepts. That's the first argument. And then the second argument is the amount of time to wait before calling this callback function, in milliseconds. So to wait 2 seconds, we provide 2000 milliseconds. And then we'll say would you like to friend user with their username from this.username:

const userData = {
  username: "Reed",
  title: "JavaScript Programmer",
  getBio() {
    console.log(`User ${this.username} is a ${this.title}`);
  },
  askToFriend() {
    setTimeout(function () {
      console.log(`Would you like to friend ${this.username}?`);
    }, 2000);
  },
};

So let's run this code. But before we do, what do you think we'll get? Note that we're writing our method exactly like we did before, but we are using a function within it.

[Run code] After two second, we see 'Would you like to friend undefined'

So our code clearly isnt working correctly. Somewhere along the way in our code, 'this' is losing its binding. It no longer references the userData object itself. So what's taking place?

As we mentioned, traditional functions, like those we are using here for our method, have a dynamic this; its value is determined by how they are called.

But what's different is that we have a function within a function in this case. What's happening here when we try to access the this of the callback passed to setTimeout within our method askToFriend. The this value of askToFriend, which we know from calling our other method getBio is userData, is undefined.

So the dynamic this of normal functions are a problem here. We need to access the this context of askToFriend, but it's not available.

So using what we already know about variable scoping rules, can you take a stab at the conventional approach to solving such a problem?

This is a difficult one to answer. The traditional approach in the past has been to grab the value of this from the outer scope, askToFriend's scope. And set it equal to a variable. Most of the time, it is called that. So this is equal to that. And then we know that any function can access any variable in any parent scope. So within our function we can say that.username:

const userData = {
  username: "Reed",
  title: "JavaScript Programmer",
  getBio() {
    console.log(`User ${this.username} is a ${this.title}`);
  },
  askToFriend() {
    let that = this;
    setTimeout(function () {
      console.log(`Would you like to friend ${that.username}?`);
    }, 2000);
  },
};

And when we run our code again [run code] it works!

However this line of setting this to that reads a bit strangely and is more of a work around than a real solution.

How can we fix this? Fortunately arrow functions provide a great solution.

The reason for this is that arrow functions don't have their own this value. Instead whenever this is used within an arrow function, they grab it from exactly where we need--the scope above where they were defined. So as compared to having a dynamic this that can change based on how the function is defined for function declarations, arrow functions have a lexical this; meaning its value is determined lexically, or in other words, by the surrounding scope.

So if we just swap out the callback passed to setTimeout with an arrow function and remove our that variable:

const userData = {
  username: "Reed",
  title: "JavaScript Programmer",
  getBio() {
    console.log(`User ${this.username} is a ${this.title}`);
  },
  askToFriend() {
    setTimeout(() => {
      console.log(`Would you like to friend ${this.username}?`);
    }, 2000);
  },
};

And run our code once again [Run code] We get exactly the answer we would want. So the lexical this of arrow functions is a great feature and it may seem great and a good reason to always use arrow functions. But sometimes you need to set a this context.

We can see this if we were to use an arrow function for our getBio method: So go ahead and do that, replace getBio with an arrow function and tell me ahead of time what we're going to get logged to the console and why.

So let's make getBio an arrow function. And if we run this:

const userData = {
  username: "Reed",
  title: "JavaScript Programmer",
  getBio: () => {
    console.log(`User ${this.username} is a ${this.title}`);
  },
  askToFriend() {
    setTimeout(() => {
      console.log(`Would you like to friend ${this.username}?`);
    }, 2000);
  },
};

[Run code] And based off of what we know about arrow functions, we get the text User undefined is a undefined. The reason for this is that we getting the properties username and title not off of the userData object anymore, but of off whatever this is set to in the scope above. Which if we log this in the global scope

console.log(this);

[Run code] We see it's the window object, the global object.

So in review, arrow functions are great when you already have a context and want to use the function inside another function. But they can't be used to create a new this binding.

So be aware of this benefit of arrow functions. We'll come across it later when we do additional work with objects and methods in our classes section.