The full video and many others like it are all available as part of our Frontend Masters subscription.

Kyle Simpson

Kyle Simpson is an Open Web Evangelist from Austin, TX, who's passionate about all things JavaScript. He's an author, workshop trainer, tech speaker, and OSS contributor/leader.

Kyle Simpson

You Don't Know JS

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

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 going to be the variables that we reference.

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.

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 going to start to maybe push your comfort zone a little bit, right off the bat, is I'm going to start to introduce some compiler terminology to you.

Because the first misconception that I want to 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.

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.

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.

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.

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.

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 going to do its execution. So it has looked at line four, before it starts to run in line three, just sort of in general.

So as we start to talk about the compiler, we have to understand that the compiler's going to 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 going to be the case.

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 going to put up a slide, and I'm going to talk about this slide for quite a while.

We might be on this slide for 20 or 30 minutes. And you're looking at that like, that's crazy, 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 going to look at this code a bit differently than you have probably trained yourself to look at this code.

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 going to gloss and hand wave over.

Compilation is all kinds of things like tokenizing, and all that stuff. We're going to 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.

That's the pass that we're going to 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 going to get a second pass through. After it's been compiled, it's going to get a second pass through where it's been executed.

So that's conceptually, how we're going to 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.

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.

It's just a single statement, we see a single semicolon. However, that's not really how JavaScript is going to 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.

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.

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.

So the compiler is going to do a single pass, or the pass that we care about, is going to 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 going to look through.

So what we're going to do is we're going to anthropomorphize, it's a fancy way of saying we're going to 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 going to teach you a slightly formal way of talking to that engine.

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

So the first line, the compiler is going to 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 want to register the foo identifier into my current scope which happens to be the global scope.

Now, I move on. I completely ignored this thing, because 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 going to care about the value.

We're going to 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 going to register the function bar into the same scope, we're currently in that scope of bar.

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.

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.

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.

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.

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.

In a sense the compiler has to kind of make a best guess, as to how you're going to use the function. It can kind of 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.

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.

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?

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.

We are going to be the naive top down compiler. So even though the real ones might not compile bar now, we're going to compile bar right now. And just as if we're being in a naive top down compiler.

Ready to take your code to the next level?

Intense courses with world-class teachers and unlimited access to our growing library of videos for the great price of $39 per month.

Get Unlimited Access Now