Lesson Description
The "Arrow Functions & this Keyword" Lesson is part of the full, JavaScript: The Hard Parts, v3 course featured in this preview video. Here's what you'd learn in this lesson:
Will explains how the "this" keyword behaves differently in regular functions versus arrow functions inside object methods. He shows that a regular nested function's "this" defaults to the global object, which is usually not desired. Using arrow functions for nested functions keeps "this" lexically bound to the outer method's context, meaning it correctly refers to the object the method was called on.
Transcript from the "Arrow Functions & this Keyword" Lesson
[00:00:00]
>> Will Sentance: Let's do it. Who shall I call on? Who wants to walk through? Michael, help me out here. We've done all of our work. Nothing's changed except that we've changed the definition of our increment function, but that's saved here. We're now going to hit our all-import line. User, well, let's do user 2 this time. Yeah, I know it doesn't say that, but user 2.increment. Where do we look for user 2, Michael?
[00:00:26]
We're looking in the global. Do we find it? Yeah. Perfect. Yep. Where do we, what do we look for on user 2? So we look for increment. Do we find it? No. Do we panic? No, we do not panic. Where do we go? We check our proto. Beautiful. Has the link to the, yep, user, what does that say? Function store, user function store. And then we call, and we find increment right there. Beautiful. And also I do like to use the term proto.
[00:00:56]
I know it's not, I just like the term proto reference versus prototype, not least because we're going to discover later on there is another, there's an object that has the name prototype, and so it's just such a gotcha. But yes, and we are going to execute that function we found, increment. Create what, Michael? Execution context. Execution context, into it we go. And in it, what's the first thing, looking back at our code, what is the first thing that we're going to do inside of the increment function now?
[00:01:28]
So we're going to declare a function called add one. Beautiful, add one, save it, yep. What's our implicit parameter? Oh, this. Yeah, let me actually put that the right way round, hold on. Yeah, we're going to have our automatic implicit parameter this, which is set to here, whatever's to the left of the dot, which is user 2, amazing. And then we have our add one function. Can anyone see where this is going?
[00:01:56]
Now let's call that add one function. I think that's right. Yeah, what tells me to call it, Michael, I know you know, but parens, exactly, a brand new execution context just inside of here, a little tiny baby one, and into it we go. Yeah, exactly. We'll just hold that for a moment. And we, no, no, that's good, and we hit what line inside of add one? This.score++. This.score++. What is this going to be assigned inside of add one here?
[00:02:44]
I hope it's the user 2 object, right? What sort of language would have it executing inside of a method called on user 2? A mission critical part of using a method, a function on an object. What does this here point to, Michael? Presumably user 2, right? I hope so. That would be nice. Unfortunately, it points to a not very useful, unused object automatically in global called window. Heard of that one before?
[00:03:26]
That is on which we would add a property dynamically called score and add one to it, which we'd have to do some type coercion because it would be undefined by default because it didn't exist before. So, do we like this? So what did people used to do? Well, they used to do something which actually worked quite nicely, but it's got to be considered one of the biggest hacks ever and gave jobs with a bad name.
[00:03:58]
Inside of increment, before calling a function inside, they would declare that = this. And inside of here we would write that.score++. There ain't no that here. We'd go out to this, that, and point to this, which points to user 2. Oh my goodness. So JavaScript did get a bad name for stuff like that. But fortunately, we're long past those days now, and we instead have an alternative approach. We have a different way of declaring a function that we said has very little effect.
[00:04:32]
It's just the same in terms of its execution, but actually it does have a specific consequence, and that is to declare it using the arrow function signature format. There we go, instead of declaring add one, our function inside of increment using the traditional function keyword, we use the arrow, the double arrow function declaration, the arrow function style. So, in that line, well, let's get rid of the add one, Michael, in that line we declare, well, what do we do?
[00:05:06]
We have our implicit this is user 2, and we declare what in the next line or in the first line of our increment function? Oh, first line increment, okay, so first line we declare a function called add one. Add one using our arrow syntax, yeah, which is just the function add one, but when we call it now, brand new execution context. What, this time in our local memory, do we think that this, the implicit parameter, will now be automatically assigned to?
[00:05:46]
So I believe it would refer to the context of increment. Exactly, it'll actually act a bit like our lexical scoping from before. It will ensure, in fact, you could say add one is now lexically scoped. It's going to have its this when it runs, its implicit parameter set by where it was saved, not by where it was, oh, that's not right, where it was. This is not going to be set by look to the left of the dot when add one runs.
[00:06:19]
Instead it's going to be set to the this assignment when it was defined. So our this in local here, it's similar, but our this in local here when we run add one is going to very helpfully not refer to the global, which when we define a regular add one function as we did here, when we ran the add one function, this referred to this window object. Now, defined as an arrow function, our this as soon as we call add one will point, the this will implicitly be assigned to where add one was defined, what was the value of this there, which was inside of the called increment where this was automatically assigned to whatever is to the left of the dot.
[00:07:05]
Because JavaScript has a very strict rule that this is assigned to whatever is to the left of the dot on which that function's being run, but our add one isn't being run to the right-hand side of a dot. And so it defaults to global, except when we define it as an arrow function, where it does get to hold on to, as its this, not global, but instead to windows, sorry, but instead to the value of this where it was defined, which was inside increment, where it was set to whatever was at the dot, which was user 2, and so our this becomes user 2.
[00:07:48]
And so, in add one now, where we have written our well-prepared this.score++, no longer do we have to go, that = this, and then do that.score++ in here. Instead we can now do this.score++. This evaluates to user 2.score++. We look for user 2 here, no, we look for user 2 here, no, we look for it out here, and what do we find? Yes, exactly, and we increment the score to 6. People, we have a very, very strong reason to use our arrow functions inside of these methods that are shared across many objects.
[00:08:26]
Not for the method itself, we heard earlier, if we defined it that way, would that change our behavior. But certainly for inside of that function, where when we call a function, it's only going to point to the this of where it's being called, which is being called not to the right-hand side of an object, it's not being called, add one is not being called as a method on our object. In fact, it's being called inside of a method on that object.
[00:08:55]
But don't panic because it was defined inside of increment with the arrow function style. Its this will automatically be set to the object in which increment was being run, the object on which increment was being run, which was user 2. We do this again with user 1. If we had user 1.increment, new execution context, into it we go, we have our this automatically set to what people, we're running increment on user 1, therefore this is set to what, Matt, automatically?
[00:09:36]
Say that again. Everyone, we're running increment on user 1, therefore our this is going to be set to what, Joe? User 1. Yeah, well, very nice voice, Joe, it's changed. User 1, exactly. Then we declared our add one function, which we know when we call it as a normal regular function inside of here, when we did this.score++, that this refers to something incredibly useless. Instead, now because we declared add one as an arrow function, the this inside of add one will also refer to the object to the left of the dot on which increment is running, so we hit this is user 1, user 1.score++, there it is, and we get 5.
[00:10:29]
Those are some bonus pieces, but really what I want to talk about here is this solution 2, using the prototype chain. What are its problems? We got objects with functions available on them. User 2 got increment, user 1 got increment, we then used them, but without having to copy them on user 1 and user 2 and user 400 and user 600. No problems, people, this is a beautiful approach. Maybe a little long-winded.
[00:11:00]
Every single, once you start thinking in an object-oriented paradigm, you move away from, or you move towards thinking about everything as data and functionality wrapped on an object. So once you start thinking in those terms, you start, you know, you might even use a function to create an object for just your single game board, even though there's only one of them, just because you become defaulting to, I will run a function or I will create an object that contains my functionality for the user's game board and the data associated with it.
[00:11:33]
And if I have to do that, if to do that every time I have to run a function that returns out that object with the data and the functionality, and to do so I have to create an object inside that, by the way, is only going to be a temporary name for it, right? I got rid of that name once I returned it out, and then I have to do this Object.create to set the bond to the function store, and I have to return out that new object.
[00:11:58]
You could think, do I really need to do all that stuff? And in other languages you don't. In other languages it's done automatically for you. We have to write this stuff every time, although it's only six words. But benefits, this is a super sophisticated, complete solution. Solution 2 is not a broken solution. Solution 2 is a fully functioning solution. However, people, it is not standard.
Learn Straight from the Experts Who Shape the Modern Web
- 250+In-depth Courses
- Industry Leading Experts
- 24Learning Paths
- Live Interactive Workshops