Advanced JavaScript Advanced JavaScript

Scope and the JavaScript Compiler

Check out a free preview of the full Advanced JavaScript course:
The "Scope and the JavaScript Compiler" Lesson is part of the full, Advanced JavaScript course featured in this preview video. Here's what you'd learn in this lesson:

The scope is where you go to look for things. The current version of JavaScript only has function scope. Kyle uses the concept of scope to help understand the way the JavaScript compiler works.

Get Unlimited Access Now

Transcript from the "Scope and the JavaScript Compiler" Lesson

[00:00:00]
>> [MUSIC]

[00:00:04]
>> Kyle Simpson: Let's get into a discussion of scope enclosures. Here first, what you need to know defini Scope, where to look for things. Now, you should rightly ask yourself a question on that first slide. What are we looking for? Well, what we're looking for are gonna be the variables that we reference.

[00:00:21] Anytime you reference a variable, like my object, and use an identifier name, somebody needs to go, and do a lookup to find out where is that variable? Where does he exist? Where was he declared? And as we'll get to, there's lots of different places, lots of different scopes that, that can exist.

[00:00:36] So that's the first question is, what are we looking for? What are those things? Well, we're looking for variables. We're looking for lexical identifiers. The other question is, who's doing the looking? And that's an even more important question. Who's doing the looking? And this is where we're gonna start to maybe push your comfort zone a little bit, right off the bat, is I'm gonna start to introduce some compiler terminology to you.

[00:00:56] Because the first misconception that I wanna completely dispel, is that JavaScript is not, it is stated that JavaScript is not a compiled language. The JavaScript is dynamic, it's an interpretive language, and that is just a false statement. So for those of you that have been under the impression that JavaScript isn't compiled, that there isn't a compiler involved, let me just dispel that myth for you, JavaScript is just to compile language.

[00:01:21] Now, it's not quite the same as how we compile our C++, or we compile our Java, or things like that. There are certainly some differences in the way we deal with it, the primary difference is that we don't distribute, that is we don't sent out the binary compiled form of our programs.

[00:01:38] Like we did with C++, We compile it into an EXE and we send it out to people. Or in Java, we compile it into byte code, and we sent that out to the Java JVM. We don't do that with JavaScript. We send the original source code program. So, in a sense it is compiled, but it's compiled every single time that it's running.

[00:01:54] But that doesn't mean that it's not compiled. Now, what do I mean by an interpreter? So the difference between a compiled language and an interpreted language is, let me give you a comparison. If any of you have ever done Bash scripting. Bash is an interpreted language, which means that when it is running in line three, it has absolutely, and no idea what to expect on line four.

[00:02:15] It hasn't even looked at line four yet, it's not there yet. It's dealing with line three. So an interpreter literally goes from top to bottom. Now there's obviously some looping mechanisms and other things that interpreters can do, through Go To and things like that. But, interpretive languages go top to bottom, just line at a time.

[00:02:31] In JavaScript however, and in other compiled languages, a compiler does an initial pass through the code to compile that code, and then it does at least one more pass through the code, and then that final pass is when it's gonna do its execution. So it has looked at line four, before it starts to run in line three, just sort of in general.

[00:02:48] So as we start to talk about the compiler, we have to understand that the compiler's gonna look for these blocks of scope, and it said that JavaScript has functions scope that blocks that JavaScript has functions. And I put that asterisk there because as we get to talking a little bit later on this section will see that not always gonna be the case.

[00:03:09] But at the moment, in the current standard version of the language you have, the smallest atomic unit of scope that we have in the language is the function. So here's one of those cases where I'm gonna put up a slide, and I'm gonna talk about this slide for quite a while.

[00:03:23] We might be on this slide for 20 or 30 minutes. And you're looking at that like, that's crazy, [LAUGH] this is a small amount of code. How could we possibly do that? I need to start introducing some of that compiler terminology to you, because you need to understand that the compiler is gonna look at this code a bit differently than you have probably trained yourself to look at this code.

[00:03:39] And I think we need to think a little bit more like the JavaScript engine, and so we can understand exactly why it does, what it does, and how it does it. So the first thing that I'll illustrate to you, is that there's a lot of complexity that I'm gonna gloss and hand wave over.

[00:03:54] Compilation is all kinds of things like tokenizing, and all that stuff. We're gonna completely hand wave and gloss over all of that stuff. But there's one important part of the compiler phase, that's really important to our discussion. And that is finding declarations of variables and functions and putting them into their appropriate scope slots.

[00:04:13] That's the pass that we're gonna think about. So what we need to do is think about this code will get passed through, once by the compiler first. And then a few microseconds later, it's gonna get a second pass through. After it's been compiled, it's gonna get a second pass through where it's been executed.

[00:04:28] So that's conceptually, how we're gonna need to think about this code. The first thing that you'll see there right on line one, you'll see what everybody will probably commonly recognizes a variable declaration. Var foo, we see the var keyword it means a variable declaration. We see an identifier named foo.

[00:04:43] We see an initialization of that variable to some value, in this case it's an immediate value, a string value. Most of you, I'm willing to bet would think of that as a single JavaScript statement, and grammatically speaking, it is true that that is a single JavaScript statement. If you go to the spec, and look at the grammar, that's a variable declaration with an initializer.

[00:05:03] It's just a single statement, we see a single semicolon. However, that's not really how JavaScript is gonna process it when it needs to execute this code, and that's the difference, that's the nuance that we need to learn to understand our code better. You need to understand that rather than this being thought of as one statement, this actually should be treated as two entirely separate operations.

[00:05:22] There's a declaration operation which we could map to saying is the var foo part, and there is an initialization operation, which is the foo equals var part. And those two operations actually happen at totally separate times. They might only be a couple microseconds apart, but they do happen at the same time.

[00:05:39] And in fact, it's not even the same mechanism within the engine that's dealing with them. And that's the key thing we need to understand, so we need to start to look at our code with slightly different eyes than we might be used to. We might be used to thinking of that as a single statement, in fact, we need to think about it as two statements.

[00:05:54] So the compiler is gonna do a single pass, or the pass that we care about, is gonna pass through looking for all of the variable declarations in all the places that it can find them. And it's not just the var declarations, but it's also these function declarations. Those are also declarations that we're gonna look through.

[00:06:10] So what we're going to do is we're going to anthropomorphize, it's a fancy way of saying we're gonna treat it as a human being that we can have a conversation with. The JavaScript engine and the various parts of the JavaScript engine, and I'm gonna teach you a slightly formal way of talking to that engine.

[00:06:28] It's gonna feel weird why I keep telling you to say it this way, but you're gonna get some practice over this in the next slide, and it'll feel a little bit more natural over the next few minutes. [COUGH] When we look at these declarations, and we say, okay, the JavaScript compiler is gonna go through, and find declarations.

[00:06:43] So the first line, the compiler is gonna come through and say, I see a variable declaration for an identifier called foo. Which current scope am I in? And the answer to that question is, you're in the global scope right now, as we see it in this code. Okay, I wanna register the foo identifier into my current scope which happens to be the global scope.

[00:07:03] Now, I move on. I completely ignored this thing, cuz that's not something I need to deal with. I move on, where's my next declaration? Okay, my next declaration is on line three, I see a function declaration with an identifier named bar. Now, as differentiated from line one where we didn't care about the assignment of the value, here we are actually gonna care about the value.

[00:07:22] We're gonna care that it is actually a function that goes along with this declaration. Those really aren't kind of separated, they're treated as one sort of operation, at least in our concept here. So we're gonna register the function bar into the same scope, we're currently in that scope of bar.

[00:07:36] Now, it's at this point that we need to talk about a nuance of the way JavaScript works because we're talking conceptually about JavaScript compilation, in the most basic and naive of terms. If you were to write just, if you were a computer science type, computer science student, and you were told to sort of write a compiler for the JavaScript language.

[00:07:58] I took a compilers' class, a compiler theory class, I got to do something like that. If you were told to write a compiler, you would just sort of do this top down approach. You just sort of skim through the code, and when you recognize the declaration you register to whichever particular scope you're in.

[00:08:12] The JavaScript compilers of today are fantastically more complex, than that naive computer science student implementation would be. So if you've ever heard of things like JITS, J- I-T, that stands for just-in-time compilation. The idea behind just-in-time compilation would say, this function bar, here, we don't see it being called.

[00:08:33] So rather than compiling the contents of the function bar, we'll just skip over it, and we'll come back to it later. We'll compile the function bar whenever we are forced to, because it's been asked to be executed. So we'll defer the compilation, and we'll compile it just in time.

[00:08:50] Even more complex than that, is that many of these engines are doing kind of started in the Firefox world, where they were tracing the performance of these things. But there this idea that you can hot swap the compilation of a function. So when they compile a function in JavaScript because of all the differences in the way that types are not enforced as strongly and as statically as they are in other languages.

[00:09:14] In a sense the compiler has to kind of make a best guess, as to how you're gonna use the function. It can kinda do some inferences about types and other fancy things like that, but it has to make essentially a guess, as to how you're doing it. As opposed to the C++ compiler, it knows exactly how that function's going to work.

[00:09:30] It knows all the inputs all the outputs, all the types and everything. So, oops, sorry I skipped back. The C++ compiler has to do, it has enough time to get it right the first time, if you will. But the JavaScript engine, it doesn't have enough time, and it doesn't have enough information, so it has to make a best guess.

[00:09:50] And so, many of the engines will make a guess, but they'll instrument that initial guess, and they'll let the function run maybe say a couple dozen times, or something like that. Then monitor how well the guess was. Did we guess correctly? Is it being used, in the way that we think it is?

[00:10:06] If the guess was incorrect, if it turns out they didn't do a very good job of guessing, they'll throw away the compilation, and then recompile the function and the hot swap and those bits directly. So there's all kinds of, and there's way more even I am totally glossing over a bunch of There is way more to it than that.

[00:10:21] We are gonna be the naive top down compiler. So even though the real ones might not compile bar now, we're gonna compile bar right now. And just as if we're being in a naive top down compiler.