This course has been updated! We now recommend you take the Deep JavaScript Foundations, v3 course.
Transcript from the "Hoisting" Lesson
[00:00:00]
>> [MUSIC]
[00:00:04]
>> Kyle Simpson: There is this conceptual model for how JavaScript works, and that conceptual model is called hoisting. As I will suggest to you, it's not really physically what actually happens. If you open up the spec You'll not find the word hoisting anywhere, because hoisting isn't actually a thing. It's a mental construct that we have invented to explain the behaviors of JavaScript.
[00:00:24] So I'm just going to present to you what the concept of hoisting is. When we look at this code, if I asked you what is the value of the variable on line 1? Or what happens, how does line 1 execute? Going back to what we already discussed earlier, back before our break, how it is gonna execute line 1?
[00:00:41]
>> Speaker 1: It won't do it first, it'll run through and define a and b.
>> Kyle Simpson: Exactly. Good catch. The fact is that it will go through first and compile this code before it executes it. We all think in terms of no it's just gonna execute lines 1 through 6 in order.
[00:00:56] But that's not true. What it's actually gonna do, it's gonna go through and find the declarations first. So, rather than thinking that a would be undeclared on line 1, in reality the best way to think about this code is that those variable declarations were moved to the top.
[00:01:12] They were treated first during the compile phase, and then the assignments were left in place. And this moving to the top thing is what we call hoisting. The fact that declarations of variables and the declarations of functions, it is said that they are hoisted to the top of the code.
[00:01:30] They're not actually hoisted, it's just that they get handled during the compile phase. So you could think of it as, the compile phase is lines 1 and 2, and the execution phase is lines 3 through 8. Does that make sense?
>> Speaker 2: So all the LHS stuff is happening at compile time?
[00:01:46]
>> Kyle Simpson: That's correct.
>> Speaker 2: And RHS
>> Kyle Simpson: That's exactly correct, yeah.
>> Speaker 4: Hey, Kyle.
>> Kyle Simpson: Yeah.
>> Speaker 4: I'm not sure if this was intentional or not, but the code me link on the previous slide for 37, links to the same thing as the one on slide 32.
>> Kyle Simpson: 37 The code embedded here links to where?
[00:02:16]
>> Speaker 4: The previous of example of the tri catch let hack.
>> Kyle Simpson: That's definitely a mistake, thanks for catching that. So, this is how the JavaScript engine conceptually could handle it or could treat the variables. This also goes for functions by the way. Let's figure out how this go is gonna work.
[00:02:40] Anybody have an idea on how it's gonna execute line one?
>> Kyle Simpson: Well first it's going to say, I've got an a. I've got an LHS reference called a. Is it going to find an a declared? Obviously yes. Then it's going to say I have an RHS reference for a b.
[00:03:04] Is it gonna find a b declared? Yes, because the function b was declared during the compile phase. So it's going to try and execute the function b. It's gonna say, I've got an RHS reference for a variable called c, is it gonna find c was declared yet?
>> [INAUDIBLE]
[00:03:24]
>> Kyle Simpson: It was declared because the compiler declared everything first. But what values is c going to have at that point?
>> [INAUDIBLE]
>> Kyle Simpson: On line one what value does c have?
>> Speaker 4: Undefined.
>> Kyle Simpson: Still undefined. So when we return back undefined, undefined is going to go in to a.
[00:03:39] Did everybody follow that? Reasoning. Let's try line 2. Got an LHS reference for a variable called c. Does it exist? Yes. Okay. Got an RHS reference for a variable called d. Does it exist?
>> Speaker 1: Yes.
>> Kyle Simpson: What is it's value at point?
>> Speaker 1: Undefined.
>> Kyle Simpson: Undefined. So we're gonna attempt to execute a function that is undefined.
[00:04:01] Which is obviously going to be an error. So did everybody see why the function expression did not get, as we say, hoisted. Whereas the function declaration did? This is the proper way to think about this code. Functions are get moved to the top first then all your variables get moved.
[00:04:19] And then you start executing as of line 7.
>> Kyle Simpson: So it's very clear from this look of the code why d doesn't yet have the function expression in it.
>> Kyle Simpson: Questions about hoisting? I shy away from saying think about code in terms of hoisting. I'm explaining it to you because it is the predominant conceptual model that JavaScript developers are taught.
[00:04:48] I think it's much better to just think about things in terms of what I've already taught you about compilers. The compiler pulls out the declarations first, so of course it's gonna find all declarations. But this is another visual way of thinking about it.
>> Speaker 2: So you can't call a function expression before-
[00:05:08]
>> Kyle Simpson: Before it's been given it's value.
>> Speaker 2: In the code.
>> Kyle Simpson: Yeah. [COUGH] Okay, now it is also true in this previous slide here. I'm sorry, slide 40. I indicated that the function b got hoisted first. And you may wonder, how do you know, as is that true, and is it possible to even tell?
[00:05:31] It is technically possible to actually prove that functions are hoisted before variables. And this next slide will illustrate, sort of a proof of that. This next slide will run. The foo call happens even though the declarations have not occurred in the code. Because both of these declarations will get moved, but if you were to think about this in terms of the hoisting.
[00:05:53] First, this function is gonna get hoisted, and then this function is gonna get hoisted which overrides it. So multiple, duplicate function declarations get overridden. But then the var foo = 2. That var foo, when it gets hoisted, it's an ignored declaration cuz there's already a variable called foo, and it hold this function here.
[00:06:15] So the way this code executes, this foo will end up running this function. It won't try to be value 2 or foo. Take away from this is don't [LAUGH] don't re-declare stuff a whole bunch of times, and don't make a bunch of, don't share the same name re-declared a bunch of different times and you won't have this weirdness.
[00:06:37]
>> Speaker 3: It's a way that the function gets written but not the variable. Does it look like?
>> Kyle Simpson: Why? Just the way it's specified. Function declarations are a declaration that implied the value comes along with it. Variable declarations, the value is left as executable code so it doesn't get hoisted.
[00:06:58]
>> Kyle Simpson: Now, some of you maybe asking why is it the JavaScript does this hoisting thing? Why does hoisting or why does the compiler have to pull out the declarations ahead of time? Why is that required? Because it seems like it might make your code harder to understand. Wouldn't it be easier if JavaScript just worked in a top-down fashion?
[00:07:14] So how many of you know what recursion is? Okay, recursion for those of you who didn't raise your hand it's fine. Recursion is when a function calls itself. Foo calls foo over and over again until some terminating decision. Was there a question at all?
>> Speaker 4: Yeah, there was a request for you to shout out the line number when you're pointing at stuff.
[00:07:35] It's harder for people to see.
>> Kyle Simpson: Okay. Trying to do that. I will try to do that more. Okay, so recursion's when a function calls itself. Anybody ever heard of mutual recursion? Not as many people. This is more of computer science geeky thing, I'm one of those people that geeks out on that stuff.
[00:07:57] Mutual recursion refers to two or more functions calling each other. And, there's a whole class, a small class, but there is a class of computer science problems which are not practically solvable. They are technically solvable, but not practically solvable without the concept of mutual recursion, because it requires tracking separate stacks, okay?
[00:08:17] So A calls B, B calls A, A calls B, B calls A, until some terminating condition when they stop calling each other, okay? So mutual recursion, if you think about it, would be impossible in a language without hoisting. Because one of the functions would always be declared too late.
[00:08:35] A would be declared before b, but b requires a, so it would have to be swapped places and then they couldn't happen. As an example here for instance, a, b, and c are calling each other in a mutually recursive way. One of these will always be declared too late.
[00:08:52] Unless there was some concept of the declarations getting hoisted. An interpreted language, if you will, could not handle this. Because it would try to parse function a. And it would say, well, I don't know about function b yet. So you would've had to put b first. And then, it could have done b and then a.
[00:09:07] But b would have required c to be first. So c would have to be first. But c requires a to be first. So it would be impossible for it to parse this if it didn't have some concept of moving declarations. Anybody ever wrote C code? Way back in the day, the C language.
[00:09:22] Remember header files? Header files are manual hoisting. You're putting the declarations at the top of what the compiler handles. Because that compiler doesn't automatically hoist. So JavaScript just automatically hoists rather than forcing you to do header files. Okay. As a slight piece of trivia, I'll give you about 15 seconds if anybody wants to try their hand at figuring out what this mutual recursion value returns.
[00:09:49]
>> Speaker 1: Something greater than 20.
>> Kyle Simpson: It is something greater than 20. You only get two guesses. And no, 42 is not the right answer.
>> Kyle Simpson: I love when I ask this question because three fourths of the room is intent, and then there's one fourth of the room that's like, f that, I'm not even trying.
[00:10:17] [LAUGHS]
>> Speaker 3: Is it seven?
>> Kyle Simpson: It's not seven.
>> Speaker 1: That's great, keep going. 14, 15, 17, 34, 35.
>> Kyle Simpson: Not 35.
>> Speaker 1: Yeh so much for me.
>> Kyle Simpson: [LAUGH]
>> Speaker 2: 36.
>> Kyle Simpson: Not 36. Five seconds.
>> Kyle Simpson: The answer's 39. Let me show you how we get there real quick.
[00:10:56] We start out passing in the value 1 into the function a. Is 1 greater than 20? Nope. So then we pass 1 plus 2, which is the value 3. We pass that into b. b gets the value 3, it passes that straight through but it puts a + 1 in the stacks.
[00:11:11] We'll come back to that one. Remember we've gone through b1, so there's a + 1 hanging out. So it passes through the value 3, and 3 times 2, on line 11, 3 times 2 is the value 6. 6 gets passed back into, a. Is 6 greater than 20?
[00:11:25] Nope, so it keeps going. 6 + 2 is 8, 8 passes into b, 8 passes straight through, we have another + 1 on the stack, so we got + 2 on the stack. 8 passes through, 8 times 2 is 16, 16 passes into a, is 16 greater than 20?
[00:11:42] No. 16 plus 2 is 18, 18 passes into b, 18 passes straight through, we got another +1 on the stack so we got a plus 3, 18 passes through, 18 time two is?
>> Speaker 1: 36.
>> Kyle Simpson: 36, is that greater than 20? So we return 36, we go back through the stack 3 times, 36 plus 3 is 39.
[00:12:05]
>> Kyle Simpson: All right, this is just illustrating. I already kinda told you about this, got you, I forget I had this slide. This illustrates for you technically the spec, it's a terrible name for it, but they call it the temporal dead zone. Between the beginning of the block on line 2, and where the let occurs, in this case on line 4, if you try to reference the variable before it has been declared with a let.
[00:12:29] That's called referencing it in the temporal dead zone and it's a reference error.
>> Speaker 2: There's no hoisting of lets.
>> Kyle Simpson: Yes. I say there's no hoisting of lets. When I said that out loud on twitter Allen on the committee was like no no no it's not hoisting at all you shouldn't call it that but they like to call it the temporal dead zone.
[00:12:50] But I would just tell you let's don't hoist, because that's the what you need to know definition.