Lesson Description
The "Scope" Lesson is part of the full, Getting Started with JavaScript, v3 course featured in this preview video. Here's what you'd learn in this lesson:
Kyle explains the concept of scope in JavaScript, which determines the accessibility of variables and functions in code. He demonstrates how JavaScript uses scope to prioritize variables with the same name based on their location within different scopes. Kyle covers global scope, function scope, and block scope, emphasizing the importance of minimizing global variables to avoid confusion and potential conflicts. He also discusses how modules in JavaScript can help manage variable scope across different files and environments like the browser and Node.js.
Transcript from the "Scope" Lesson
[00:00:00]
>> WebDevSimplified: Now that kind of wraps up the basics of defining functions. So now we're going to move into a section on scope, and this is really about more advanced features of functions themselves. So scope essentially determines what variables and functions can be accessed inside of your code at any given time. So we take a look at this code, I'll just copy it into Visual Studio Code so it's a little bit easier to read in the formatting.
[00:00:22]
We have a function called sayHi. Inside of here we create a variable called result. We set its value to high, and then we log out what that result is. Also, outside that function, I've created another variable with the exact same name result, and I've printed that to be bye. So when I call sayHi, you may wonder, okay, what is the thing that gets printed out, because now I have two variables with the exact same name.
[00:00:45]
This is where the idea of scope comes in. JavaScript is able to look at what variables are accessible at all given times to determine which one should be accessed. And in our case, since result is here, it's the closest variable inside this function, it's going to use the version that prints out high, while down here, it uses the version that prints out bye. So you can see in our output we get high then bye being print out because this top one prints out high while this bottom one is going to print out bye, and this is because it doesn't have access to the variable defined up here.
[00:01:13]
So how do we know what scope variables are available inside of our different functions inside a JavaScript? This is essentially determined using curly braces. Everything in JavaScript, anytime you see a curly brace, that defines a brand new piece of scope. So when we create a function, we use curly braces. That means that function has a specific set of scope inside of it. Everything in here is a new scope.
[00:01:34]
We also have what's called the global scope. That is the scope for our entire file because obviously we have code in our JavaScript that doesn't have any curly braces around it. For example, x equals 10 and my function itself doesn't have curly braces around it. These are considered globally scoped variables and functions that are pretty much accessible everywhere. Now determining which variables are accessible in these various different scopes because now we have this variable scope here and we have this overall global scope, we need to realize that scope is essentially a one way street.
[00:02:05]
I like to think about it as one-way mirrors. You can always look out the one way mirror, but you can never look inside the one-way mirror, and that's exactly how scope works. You can always see things that are outside of your current scope, but you can never see things inside of scopes inside of you. So this function right here which we call the func, it creates its own scope inside these curly braces.
[00:02:26]
So everything inside of here is a smaller scope and inside scope and everything outside of it is an outer scope that's outside of it. So the nice thing is, since scope can always look outward inside our func, we're able to access variable a because we're looking outside of our current scope to get this global scope variable. But we can never look in. For example, here, I have a variable const b equals 2.
[00:02:49]
This is scoped to this function right here, and I'm trying to log out that variable outside of that current scope. And since scope can never look inward, I don't actually see anything inside this function. So there's no b variable being defined at all, and this will actually throw an error when my code, because again this b variable does not exist. Now you may notice a slight quirk if you actually copy this code over.
[00:03:09]
It won't technically throw you an error, but it won't actually do what you want. Sometimes it may throw you an error like it is here, but sometimes it may not. Again, it's a little bit interesting, but we don't have access to this b variable because it's inside this scope. The way that JavaScript essentially looks at it is like this: you can see I've collapsed that down. That's a toggle you can use on the side just to make your code easier to read.
[00:03:28]
But when I'm outside that function, JavaScript essentially looks at my code like this. It says, okay, we have a function called func, and that's it. I don't know what this b variable is because I can't look inside of other people's scope. It's this one-way mirror trick that's always looking outward and never looking inward. Now in JavaScript there are multiple different types of scopes just to make things even more confusing for you.
[00:03:50]
There is a global scope, a function scope, and a block scope. The nice thing is we really only have to worry about two of these: the global scope and the block scope, and the function scope is really niche and is only used in a few scenarios which I'll be talking about when it becomes relevant. Global scope luckily rather straightforward. It's just the things that are available globally in your application.
[00:04:07]
Essentially anything not wrapped in a curly brace is your global scope. So this name Kyle is a global scope so it can be accessed anywhere at all because it's global to your entire application. Same with the sayHi function, it's not wrapped in any curly braces, so it's available anywhere inside your code. A block scope is just like we talked about. Anything wrapped inside of curly braces defines a brand new block scope.
[00:04:29]
So here we have a function. It uses curly braces. It creates a block scope. Here we have an if statement, which we'll cover later in the course, but you use curly braces to define those. So now we have a brand new scope. And something interesting about JavaScript is you can put curly braces anywhere you want to create your own scope. So here I just have curly braces on their own, and that creates a brand new scope.
[00:04:48]
So now I have this x variable defined in three different places which are all their very own scope, and they just are whenever you have curly braces, you create a new scope. And if we just copy this, we can even nest our scopes directly inside of each other. So here we have a scope and inside this scope, I can just come in here and I can say const y equals 2, and if I spell that properly. There we go.
[00:05:09]
Now I have my scopes nested inside of each other, so I have this scope, which is inside of this scope, which is inside the global scope for my entire application. So we have this kind of nesting that we can do throughout all of our different code, which is really useful to understand and we'll get a little bit more in depth into how that works. Now finally we have function scope. The only difference between function scope and block scope is that function scope works for the entire function, even if there's nested scopes inside of it.
[00:05:34]
So a function scope is just everything from the starting curly brace to the ending curly brace of that function. Even if there's other scopes, they still fall within that function scope. Like I said, not something you're going to use very often, but there's a few niche scenarios I'll cover later where you will use the function scope instead, but pretty much everything is either block scoped or it's going to be in that global scope for you.
[00:05:54]
And like I talked about, we have nested scope. Here's kind of an example of what that would look like. We have this c variable in a global scope. Then we have a set of curly braces here which defines this new outer scope is what I called it, which defines the variable a. And finally we have this inner scope which is inside of this other scope, so they're nested inside of each other, and this defines a variable c or b, sorry.
[00:06:15]
And we can log out a, b, and c just fine because we have access to everything outside of our current scope. You can always look outward inside your scope. But if we come down here, we don't have access to that variable because that's in an inner scope and we can never look inward. Generally with your code, you're indenting your code every time you create a new scope, so you can almost think that you can only ever look at code that is at an indentation that is further out than your current level of indentation.
[00:06:40]
You can never look inward on that current indentation. Now kind of wrapping around to our very start where we had two variables with the exact same name. How does JavaScript know which one of those variables you want to use? Again, it uses scope to try to figure that out. I'm going to copy this code over so we can analyze a little bit more in depth, but you can see we have a const result which is set to Kyle.
[00:07:00]
This is in our global scope. Then we have a sayHi function which again since it has curly braces, creates a brand new scope for us to work with and creates a new variable called high plus whatever the name is, it's a different variable, and you can see inside of our code we're calling sayHi, passing it in Kyle, so it prints out hi Kyle, and down here we're logging out result which prints out our global variable which is Kyle.
[00:07:20]
So the way JavaScript tries to determine what variable you use when there are multiple variables with the same name is it always starts with the smallest level of scope and tries to find the variable there, and if it can't, it'll constantly work outward until it finds your global variable. So in our case, when we try to log out result, what happens is JavaScript says, okay, I need to find the value of result.
[00:07:40]
So it first checks inside of your scope that you're in, which in our case is the sayHi function. And sure enough, there is a result variable right there, so it uses that result variable to render out what our code is. But if this result variable didn't exist, instead, what would happen is it would look inside of here and say, I don't see a result variable. Now let me go to my next level of scope. So it would move one layer outward, scan this entire file and say, oh, you know what, here's a variable called result.
[00:08:03]
I'm going to use that particular variable. So it always starts with the most inner level of scope and works its way out until it finds where that variable is currently being defined at. So some best practices here is you generally want to minimize the amount of things inside your global scope. The main reason for this is because if we have something like result defining our global scope, it's available everywhere in our application.
[00:08:26]
I mean literally everywhere. And if you start defining global scopes left and right, as your code gets more and more complex. Now all the variables in your code are available everywhere, so it's really confusing to know is this variable what I want it to be? Has this been changed by something else? What even variables are available to me? There's so many different variables that are available. So generally I like to take my variables and move them as close and inward scoping as I possibly can.
[00:08:48]
Obviously some things must be global, but try to minimize it as much as you possibly can. Here's an example of some code that I see really commonly from, you know, people that are getting started with JavaScript is they create this name variable out here globally and then they use it only one place inside this function. Well, it would just make more sense to put that name function or name variable directly in the function because that way you don't have this global variable that's confusing other parts of your code.
[00:09:14]
Another thing is, even though JavaScript is smart enough to figure out that this a variable and this a variable are different, I generally try not to name my variables the exact same thing inside of scope, especially when they're really close to each other inside your code. It just makes it hard to know which one am I using at this current time, and you have to think about scope and scope is confusing, so I don't like to think about it.
[00:09:32]
So generally when I have different variables that mean different things, I try not to share the name and I try to give them distinctly different names. Sometimes naming them the same thing makes sense, but more often than not using different names will just make your code easier to understand, because now I know outer a is clearly different than inner a, and it's easy for me to reason about which one is the correct one to use in this particular circumstance.
[00:09:56]
Any questions about scoping before we move on, because again this is a rather complicated topic. Yes, I just want to clarify that global only refers to the entire file. It doesn't refer to anything outside the file. So there's not a, there's not, there are not variables that are in other files that you can create that are called global. So technically, let's, this is a great example here. I'll just come into my index file.
[00:10:22]
I'm going to create a brand new script file. We'll call this one script 2. And over here, I will create that script file. Script2.js. There we go. Whoops, of course I didn't actually put the 2 in there. There we go. So now inside this script, I'm just going to say const a equals 1. All I'm doing is defining the variable a in this script, and in script 2, I can actually console.log a and you'll see it actually prints out one to my screen.
[00:10:44]
So when you create a global variable, it is global to every file loaded on your web page. So again, another reason why global variables can be very confusing to work with because now they're global everywhere across your application. And this is why your loading order for your JavaScript is actually important because if I put script 2 above script 1, the variable hasn't been created yet and now I'm going to get an error.
[00:11:04]
So this is why the defer keyword is really important because it makes sure your script files load in the correct order. So if I depend on something from this script in this script, I know that they're loading in the correct order. That answer your question? I kind of, so you're saying that there's a, global is like global for multiple files, but I, it sounds like even if you've made a different global variable in the first script file, it might override, one might override the other.
[00:11:38]
Is that right? Or an error I believe, let's see here. If I come in here and I just say const a. We'll set this one to 2 and we'll console.log a. This isn't something I do very often, so we'll just do this live to see what happens. And yeah, we are getting an error because essentially it's seen that this one currently already exists, so you're trying to overwrite a variable that already exists. So you are going to get an error if you try to.
[00:11:57]
And so the other one loaded first, you would still get an error, but it would be because it was declared. So yeah, if I swap these two around, it would give me an error inside the second file essentially. Anytime I try to recreate that variable, it's going to give me. Thank you. Yeah, because it's being assessed in the browser altogether. Yep. So essentially what's happening is it loads your script file and it just runs the entire thing.
[00:12:18]
And essentially JavaScript almost treats it like they're the exact same file. Like you can almost treat it as this code just being added to the end of this file. That's kind of how JavaScript runs it. So you can see when they're in the same file, it's very clear to see this as an error. So JavaScript just runs your code from top to bottom. Now technically something I'm not actually covering inside this workshop because it's quite a bit more advanced is there's a feature of modules inside JavaScript which actually allow you to break out of this paradigm.
[00:12:43]
If you use modules, anything that you declare globally in your file stays only in that file. It doesn't leak out to your other files. So if you start to run into this problem when you're having lots of files with lots of global variables, looking into modules, they're generally called ES modules in JavaScript. It's going to be a great way to solve that particular problem, but JavaScript on its own does have that problem where it's going to leak out your variables globally across the entire HTML page itself.
[00:13:07]
Any JavaScript you load, even JavaScript from someone else, if you download someone else's JavaScript. If they have global variables, they show up inside of your code. So again, very confusing to work with. Is Node just considered a different execution environment than the browser, so the browser's running, it would have its own globals and then Node on the server would have its own globals? Correct.
[00:13:25]
Yeah. Node is an entirely separate process. You can almost think about it when you're running your code in the browser. It runs just on the browser itself. Don't even think about your coding editor or anything like that. It's running just on the browser itself. And then if you had a backend running in Node.js, that's running on a completely different computer somewhere else, maybe even entirely in the world that may be running in a server rack in, you know, Amazon's warehouses or something.
[00:13:47]
So they have completely different environments, so they're not going to share with each other. And even if like two people access your exact same website, they have their own browser, so they have their own variables inside of it. It'll run the same code, but obviously they will be two separate environments from each other. Apart from this, are there any other types of scope in JavaScript? So no, those are the three main ones that you have to worry about.
[00:14:08]
You have your global scope, you have your block scope and your function scope. Technically, when you start working with modules, there is, I believe, depending on the browser you use, they may call it different things, but it's like a script scope or a file scope. So essentially it replaces your global scope with a scope just for that one file. That's only when you're working in that module file way of doing things, which is again a bit more of an advanced feature of JavaScript, but essentially it's a global scope that only applies to one file instead of every file on your page.
[00:00:00]
But really for the most part you have to worry about global scope and block scope, and those are the two that you have to worry about. And if you're using modules, you just think of your global scope as the file. If you're not using modules, global scope is your entire web application.
Learn Straight from the Experts Who Shape the Modern Web
- 250+In-depth Courses
- Industry Leading Experts
- 24Learning Paths
- Live Interactive Workshops