Deep JavaScript Foundations, v3

Compilation & Scope

Kyle Simpson

Kyle Simpson

You Don't Know JS
Deep JavaScript Foundations, v3

Check out a free preview of the full Deep JavaScript Foundations, v3 course

The "Compilation & Scope" Lesson is part of the full, Deep JavaScript Foundations, v3 course featured in this preview video. Here's what you'd learn in this lesson:

Kyle walks through compilation of code using a silly exercise to illustrate a mental model for scope.


Transcript from the "Compilation & Scope" Lesson

>> Kyle Simpson: To understand this a little bit more concretely, I wanna walk through a piece of code which within a few seconds of glancing at you can in your mind have executed and said I know what this program is gonna do. But it turns out there's a lot more complexity to this code than we are typically used to dealing with.

And I'm gonna try to train you over the next few minutes to think about this code in a way that more closely matches the way the JavaScript engine thinks about it. So we're gonna do that by way of what's gonna sound like a bit of a silly exercise.

But hopefully it'll be lighthearted enough to keep you engaged and interested. We're going to pretend as if a conversation is happening in this processing of the JavaScript program. There's going to be two actors, two entities that are going to be talking. One is the compiler, the thing that's processing the JavaScript program.

The other one is the scope manager, and scope manager is the one that makes buckets, makes marbles, drops the marbles in. It's the compiler who says, hey, I have this thing, and it's the scope manager who says, I'm gonna make a plan for that, I'm gonna make a plan for a bucket and make a plan for a marble.

And that'll be our first pass through the program, is that compilation step. And then after we've set up all those plans, then we'll come back and execute the code. So we want to process this code according to that conversation. And you can follow along with me, you can literally speak out loud.

It helps you, it's sort of an old teacher trick, if I get you to say this stuff out loud, it embeds it more in your brain. So as we go along I want you to speak out loud with me, even though it sounds a little bit silly. Remember, in our processing phase, in our compilation phase, we have a scope manager and we have a compiler.

So I'll play the part of compiler talking to a scope manager. And when I come across, as I see here on line 1, when I see on line, a var declaration, whether it's a let or a const, or a var isn't relevant at this moment. But I'm gonna use vars just for the simplicity of illustration.

That's a formal declaration. In other words, we're creating a marble. And we gotta know what color to make that marble, so we gotta have some scope that we're adding it to. And in this case, obviously it's the global scope, okay? And just for the simplicity of our discussion, I'm gonna use the three primary colors.

I'm gonna say the outer global scope is gonna be the red bucket and then wherever we have inner buckets we'll have blue and green, okay? So we have a red bucket that represents this global scope and I, being the compiler, whenever I see a formal declaration like I do on line 1, I'm gonna say, hey, global scope, hey, red bucket, I have a formal declaration.

I have a marble here, have you ever heard of this thing called teacher? And I'm asking this question because if it already knows about an identifier of the name teacher, it doesn't need to do anything. That's a no op. There's no such thing as redeclaration. It's just okay, great, you already know about this marble.

But in this particular case, since it's the first time that the compiler would have asked the global scope about a variable called teacher, then the global scope's gonna say, nope, never heard of it. But I've created now a red marble for you and, pop, now we just dropped it into the red bucket.

It hadn't actually been created, that they don't get created for real until execution, but conceptually we're creating this plan from what we see in the program. So yes, global scope says, sure, we'll make a red marble and we'll drop it into the red bucket. Now what we're doing is looking for these formal declarations.

And sometimes they look like on line 1 var teacher. Sometimes they look like line 3, functions or another kind of declaration. Functions make a marble, in this case, the marble on line 3 would be called otherClass. And that needs to get added to some scope. So if I ran across, as the compiler, line 3, a formal declaration for a function, I would have the same conversation with the scope manager.

I'd say hey, scope manager, compiler here again. I found another formal declaration, in this case for a marble called otherClass. Have you ever heard of that identifier before? And in this case, what's my answer gonna be?
>> Speaker 2: No.
>> Kyle Simpson: Nope, never heard of it, but here's another, what color marble?

>> Speaker 2: Red.
>> Kyle Simpson: Red marble because we're still in the global scope, so here's another red marble, boop. So now we have two red marbles in the red bucket. That takes care of the identifier otherClass as well as the identifier teacher. Now, I being the compiler am smart enough to realize, that's a special kind of thing, because a function makes scopes, it makes buckets.

So, hey, scope manager guess what, we're gonna need another bucket. And to stretch this metaphor a little bit, now we're creating a bucket inside of a bucket, so like a giant barrel and then a tiny little bucket inside, okay? I know it's stretching the metaphor a little bit.

But now we had a giant red barrel and inside of it, hey, scope manager we need a blue bucket, because it's a function. So scope manager says, sure, now we got a blue bucket, and now we are talking about the blue bucket, and let's step into that function and talk about it as its own scope, since that's functions creates scopes.

So where's our next formal declaration then? Well, it's on line 4, right? Line 4 has another formal declaration, in this case for a variable called teacher. So, the conversation is gonna continue exactly like before, even though it's the same name, same conversation, we're gonna say, hey, blue bucket, hey scope manager for this other class.

I have a formal declaration for a marble called teacher, ever heard of it? Now the answer to that question might surprise you because you might think, sure, we heard about it on line 1! But remember we're talking to an entire different bucket now. We're talking to the blue bucket, not the red barrel, okay?

I know this stretches the metaphor horribly thin, okay. But we're talking to the blue bucket. So we're saying hey, blue bucket, do you have a marble called teacher? And what's blue bucket's response? What's the scope manager going to say?
>> Speaker 3: No.
>> Kyle Simpson: But here's your, what color marble?

>> Speaker 3: Blue marble.
>> Kyle Simpson: Here's your blue marble. Okay, so now there are two marbles in two separate buckets of two different colors, even though they have the same name. Follow me? By the way, having two variables at different scopes of the same name, that has a term, it's called shadowing.

Sounds like sort of like evil or whatever, there's nothing bad about it, shadowing is entirely okay. But there is an offshoot of shadowing, which is, now that we've created a variable called teacher, we've created a blue marble called teacher in that other class scope. Well, now there's no possible way that we can reference lexically the variable from line 1.

We can't reference the red marble because now there's a blue marble of the same name, because of the shadowing. It's totally okay, but it does limit us from what we can access. Because those names, it would match the nearest one, which in this case would be the blue marble.

Follow me? So, we look for any other formal declarations. Do we find any more in the otherClass function? We see a reference to console.log, but we see no more formal declarations. So we're finished with the otherClass function. Now we step back out to what color scope?
>> Speaker 2: Red.

>> Kyle Simpson: The red scope, the global scope. And we look for the next formal decoration. What line do we find the next formal decoration on?
>> Speaker 2: 8.
>> Kyle Simpson: How about line 8? You see here the formal function declaration for an identifier called ask. The conversation continues exactly like before.

The conversation says, hey, what scope? Hey?
>> Speaker 2: Global.
>> Kyle Simpson: Global scope, hey, red bucket, I have a formal declaration for an identifier called ask, ever heard of it? And the answer is?
>> Speaker 3: No.
>> Kyle Simpson: Nope, never heard of it. But here's your?
>> Speaker 3: Red marble.
>> Kyle Simpson: Red marble, great, you're following perfectly.

There's a red marble, because we are in the global scope. And that's what color marble matches with the red bucket. Yes?
>> Speaker 3: Why did we skip over console log?
>> Kyle Simpson: We didn't skip over it from the perspective of compilation. That is a good question. The compiler would have certainly handled line 5 and done a bunch of compilation.

It doesn't have any impact on our scopes. So we've narrowed our focus of compiler theory to preparing our identifiers and our scopes. Doing our marble bucket sorting thing. So we're just skipping over the uninteresting details at this point, right. There's whole courses on compiler theory that would get into all the nitty gritty of line 5, but since it doesn't create or access any variables within our scopes, we don't need to worry about it.

Make sense? Okay, good question. All right, so we just made a red marble called ask. And being a good compiler we notice that's attached to a function. Guess what, scope manager, we're gonna need another bucket. This one we'll call the green bucket. So scope manager, could you go ahead and make a green bucket for us.

Scope manager says sure, and now we step into the scope of the green bucket, the function labeled ask here, okay. So we look for formal declarations in that function. Where do we find the next formal declaration?
>> Speaker 2: Line 9.
>> Kyle Simpson: Line 9, you see there on line 9 I have a var declaration, so that's getting real repetitive, the conversation goes exactly the same way.

Hey green bucket, hey scope of ask, I have a formal declaration for an identifier called?
>> Speaker 2: Question.
>> Speaker 3: Question.
>> Kyle Simpson: Question, ever heard of him? And the answer is?
>> Speaker 2: Nope.
>> Kyle Simpson: But?
>> Speaker 3: Here's your green marble.
>> Kyle Simpson: Here's your green marble, right? So we get a green marble and boop we drop it in the green bucket.

Now, this is critical, because you'll notice on line 10 we have a reference to an identifier. This isn't creating marbles, so in this processing step we're not gonna worry so much about creating it. But we are going to have to understand where that marble comes from, what color marble that is, when we execute the code in the next step.

So it's critical that we do this marble sorting correctly as we process the code the first time. So we're done with the ask function, right? There's no more formal declarations. And then we step back out to the global scope. Do we find any more formal declarations in the global scope?

>> Speaker 2: No.
>> Kyle Simpson: Okay? So thumbs up, we are now done with that processing stage, with that compiler phase of this code. And what we are left with is a plan for all the buckets and all the marbles. We've accounted for all the scopes that exist and where all the identifiers fit into all of those.

Including references to them like on line 10, we know what color marble that is. You with me? We know all of that stuff. We've got all that plan all laid out. That is then handed over as part of the execution plan so that the virtual machine, the JavaScript engine, can run this code.

Everybody with me? Now it's important to note that when we execute the code, there's no more declarations for anything. All the vars are gone, essentially, because we don't need to declare anything anymore. We already know what that's gonna do, because we figured that stuff out at compile time.

So, one key takeaway, don't miss this. One key takeaway is that in a lexically scoped language which JavaScript is all of the scopes that we're dealing with, all of the lexical scopes and identifiers, that's all determined at compile time. It's not determined at run time. It is used at run time, but it is determined at compile time.

And why that matters is, that allows the engine to much more efficiently optimize, because everything is known and it's fixed. Nothing during the run time can determine that this marble is no longer red, now it's blue. Once we've processed through, we already know what color marble it is and we're done with that discussion.

So that allows the JavaScript engine to be much more efficient at its job. The takeaway that you should have from that is, the decisions that I've made about scope are author time decisions. When I write this function and I put this variable here, it means that that variable is always gonna be that color marble.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now