Check out a free preview of the full Vanilla JavaScript Projects course
The "Scope & Closure" Lesson is part of the full, Vanilla JavaScript Projects course featured in this preview video. Here's what you'd learn in this lesson:
Anjana explains the scope in JavaScript. Closure, where a function can "remember" values from its parent scope, is also discussed. Understanding these concepts can help developers avoid unexpected behavior in code.
Transcript from the "Scope & Closure" Lesson
[00:00:00]
>> So what happens then, if in the next line of code, I changed the value of level and I change it to error. And so now I'm trying to really weary the user, you've made a terrible mistake you shouldn't have done this. I still get a warning. Even if I changed level, I still get a warning when I call where a user.
[00:00:26]
And this is a similar issue to why earlier we were still getting the same values for the position of our beautiful clock. Even when we were running the callback function over and over again. So what's going on here? It all comes down to scope. So in JavaScript, [LAUGH] as in most programming languages that we would find ourselves using, there are essentially a variety of, we could think of them as contexts called scopes, where JavaScript knows what you mean when you say a thing.
[00:01:12]
For example, it knows what value I'm referring to when I say level, only because I declared it earlier in my global scope. And so we talked about this a little bit in JavaScript first steps so might be worth a look. But I like the metaphor of a limousine for JavaScript scopes, and we can just refresh our memories of like how Scope works in JavaScript.
[00:01:40]
Let's move over to the, whoa, let's move over [LAUGH] to the virtual whiteboard, shall we? Okay, so we've got our, when we're in a particular window or what have you, we have our kind of global, I hope you can read what passes for handwriting. We have our global scope, right, where I can do things like maybe I've declared like level is a thing.
[00:02:11]
And that points to some value over on the heap, right, we now know. So level points to some value that's like, well, I guess, I should have used, yeah, okay, so it's warn. Then I have a function, and what did I call it? Makelogger. And inside of this function, JavaScript create, or when I declare this function, JavaScript in the body of that function has sort of moved into another scope.
[00:02:45]
And I like the limo metaphor for this because basically makelogger, whoa okay, can see outside of its scope to the level variable that I declared. So if I call level in here, it's still going to be the same value that's declared in the global scope. I can actually access that value.
[00:03:20]
Whereas if I declare another variable, like, that's hard to read, let's say I do a cons new var equals blah blah blah, and then after defining my function in the global scope, I try to access new var, it's not gonna happen. So imagine you're in like a stretch limo when you're in this function.
[00:03:48]
You can see out the tinted glass, but nobody can see in to you and whatever life of luxury you're living there. [LAUGH] So this is sort of this concept of these nested scopes, where a function can access things that don't just exist in its scope. Becomes very important when we think about when and where JavaScript is looking for these values.
[00:04:14]
So to go back to our whiteboard when I instead of declaring a new variable, I have declared some makelogger function. And that's going to return a function, actually we've got another little scope nest, hello, okay. That's gonna be, sorry, this is the log function. And this function within a function can also access what's in both the makelogger scope and what's in the global scope.
[00:04:58]
This function that I'm declaring, In here within the makelager function, we've got kind of like a limo within a limo within [LAUGH] a limo. So the metaphor breaks down, but [LAUGH] the idea is again like we have access to other scopes than the one that we're in when we're inside.
[00:05:26]
Not necessarily when we're outside, well, not when we're outside, we can't see into somebody else's scope. But the fact that we can see out means that the values and the decisions, that we're gonna be making inside of this inner function might be based on values as they were in the scope where we defined it.
[00:05:52]
So let's look at this. So, okay, we have level in the global scope. We then call makeLogger with this value level. So now, makeLogger, in its little scope, it declares a new variable, log level, that's defined by, this is created by the way JavaScript function declarations work. We can just declare the variable right there as an argument in the middle of the parentheses.
[00:06:21]
So now I have access to the variable log level within the makeLogger function. For example, if I wanted to put a console log here before returning, I could, and level would be warn. But also, that value gets referenced when this log function is defined in order to look up the given string as a method on the console object.
[00:06:54]
So what's happening is that when this function runs, let's go back to the whiteboard. Clean this up a little bit, okay. [COUGH] So this was called, I don't know, log level was another variable in here. And that points to whatever was level before, cuz that's the value we passed in, which then ultimately points us back to this string warn.
[00:07:27]
Pretend that's an arrow, [LAUGH] and the log function then is going to say, okay, I am now a function. Where I'm trying to do console and then whatever log level is and so this is where it's like okay what is the value of log level let me go look for it.
[00:07:51]
I found it eventually through a series of pointers now I'm gonna replace this value in here and the function that's returned is pointing to that value. Even if I later change it down here in the global scope, If I change it over here, this is just gonna repoint this variable and not change anything about this function that was already created and stored as its very own object on the heap.
[00:08:36]
Okay, so I fill out a very chaotic arrows. But the point here is that, We can essentially remember data from the context in which this particular, in this case function, was declared. But regardless, when I looked up this value level, later renamed to log level, and I got that value, and then I used that value in the code that I then stored as a function returned as a value, which I could then later call.
[00:09:23]
That string, because here we're dealing with a string gets remembered even after level has long changed. So where user is remembering something that's kind of stuck in time at whatever it referenced when it was first running through its code, which needed to reference values that were pointed to by certain variables.
[00:09:55]
So this is all to say, essentially, what we have is Jurassic Park and a value of our little warn string trapped like a mosquito in the metaphorical amber of this function value that was returned from the parent function. So we can put aside the fact that this may not have any scientific accuracy of how mosquitos and dinosaurs work.
[00:10:25]
Shockingly, the Jurassic Park was a non-fiction film, but anyway [LAUGH]. The idea here is that we can trap things inside of a function that we have passed back out of a parent function as a value. And that is, to add some more jargon on us, a concept called closure.
[00:10:55]
So while the words are sometimes used differently, a bit like essentially what we're saying is that this worry user function, this internal log function, the internal log function that is the value that I'm getting back when I call my mmakeLogger has closed over. Or sort of it is encased in amber the value from the scope where it was defined.
[00:11:29]
So not the value in the global scope as many times as it may change but the value that was pointed to by that variable in the global scope at the time that this inner function was declared. And it doesn't have to just be something from the global scope.
[00:11:47]
It could also be a variable declared within the parent function or within the parent of a grandparent or what have you. So any of those outer scopes that the limo can see out to, the infinite nested limo, the Russian doll of limos, if you will, can be closed over and trapped in amber and remembered for the lifetime of that function itself.
[00:12:13]
Okay, so this is a little bit of scope review that we have different scopes. It's also a bit of this is why we need to remember that if we want a new random value we have to call it in the right place. Or if we want to not call a random value too many different times, we can also declare it or get that value in a higher scope and then pull it down into our little ambery nugget of a closure.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops