Lesson Description

The "Multiple Closures" Lesson is part of the full, JavaScript: The Hard Parts, v3 course featured in this preview video. Here's what you'd learn in this lesson:

Will walks through instances where multiple closures are created. JavaScript functions create local execution contexts that reset each time they run, but when an inner function is returned, it carries a persistent private memory ("backpack") with it. Each call to the outer function creates a new execution context with a fresh local variable, but the returned inner functions retain their own copies of that variable, independent of the outer function's subsequent calls.

Preview

Transcript from the "Multiple Closures" Lesson

[00:00:00]
>> Will Sentance: We're moving from our previous code here, where we ran outer, put our add one function definition into new func, but brought with it the backpack of live data from when we saved a one inside the execution context of outer's call, and then we run new func twice. I've just erased it here so I can fit in the code below, but let's just make sure we've done it here again. We ran new func once, looked for counter in the local memory, didn't find it.

[00:00:37]
So we headed off to our backpack of new func, which is here, and incremented it to one. We then ran new func again, I'm putting it just adjacent so we can fit it all on our blackboard, where we hit counter plus plus again, and in local memory, we didn't find it again, but we headed off into, sorry, not into global, into new func's backpack and incremented from 1 to 2. We then popped new func off the call stack.

[00:01:18]
Let's tidy that up a bit, and now things get really interesting. Now we are going to call outer again. So we've only done here, all we've done is move the new func calls up to the right and run another func twice, the result of a second call to outer. Brand new call to outer. Note the result of a second call to outer. Another function is not a call, is not the call to outer, it's not the second call to outer, it's the evaluated result, the return value of the second call to outer.

[00:01:55]
You can see, I hope, start to see why that precision is so important when you're dealing with returning functions from other functions. Okay, so let's go, Brady, can I call on you here? Let's go from our constant another function is the return value of calling outer. Talk me through a little bit here. Brady, what are we doing in that line, left hand side first? So we are declaring a constant on another function.

[00:02:26]
Bottom. And do we yet know what's going to be stored in it? Not yet. Okay, because we've got to go and do what? Invoke the outer function. Excellent, very good, and we're going to invoke it and create what, Brady? A brand new, everyone together. Say, help everybody out, everyone together, a brand new execution context. Yay. That always enlivens me, I can only imagine what it does to you. Into it we go.

[00:03:03]
And in the local memory, we're going to do some stuff, but firstly, what do I add to my call stack, Brady? Because we're calling outer, outer execution context. Yep, the call to outer is on my call stack, that's where we're at. I'm inside of outer. What's the first thing I save inside the call to outer, Brady? We assign the value 0 to the variable counter. Beautiful, there it is. What's the next thing we do inside of outer, Brady?

[00:03:32]
We declare the function add 1. Beautiful, there it is. And I ain't going to get this wrong, people. Joe. Actually, no, just because, Matt, you were taking a lead on this for a while. Matt, when I define my function add one inside the call to outer, before I return it out, what does JavaScript immediately do? What does JavaScript immediately give that function? Well, it creates that closure to that counter.

[00:04:02]
Mhm, it creates that hidden bond via what hidden property? Scope. Scope, square bracket, square bracket, scope, square bracket, square bracket. Beautiful. Nice. And then we hit return add one, which is going to return out that function. There it is. And it's going to go look for add one, find the function, return it out into what global constant, Matt? Perfect. Another function. But we didn't just get a function pulled out, we also got pulled out what on its back, Matt?

[00:04:46]
A backpack, the official term. There it is on its back. I'm going to be shouting that till the end of time, with counter with what value, Matt? Uh, 0. 0, spot on, in it is. Amazing. That's it, right? Then we finish with outer, Matt. Yes, yep, close the execution context. Should have just closed this one as well. Close the execution context, pop it off the call stack, pop it off the call stack. Oh, there we go, and we're back in the global, where we are now going to call, Matt, what function?

[00:05:26]
Uh, another function. Another function, there it is, another function, new execution context, added on the call stack, and what's the only line of code inside of it? It is a counter, and we add one to it. Yes, where do we look first? We look locally and don't find it. Excellent. Where, what bit of another function do we then go and look at, and you can call it the term you want to call it? Oh, closure.

[00:05:55]
We go look in the backpack, the closure, where we find counter. Well, let's pause. I think I want to pause here because I want to ask, well, yeah, okay, fine, yeah, what's, and it does what to counter? Uh, adds one. Exactly, adds one. I guess let's just do one more call so that execution context goes, anything in local memory, there isn't anything, but if it'd been there, it would have been deleted.

[00:06:21]
And we go to another call to another function, where we hit counter plus plus again, looking local, it's not there, looking another function's backpack, find counter is what, Matt? Uh, 1 plus 1, which is 2. Beautiful. Okay, so with that in mind, I like this as a question, so I'm not quite sure how we got the answer written up already. With that in mind, in this code, I call new func once, I call it again.

[00:06:48]
New func was a result of my first call to outer. I call it again. Then I call outer again, sorry, then my second call to outer, I get the output add one into another function with a brand new backpack of live data. I call another function once, I call it again. If I were to be console logging at the end of my add one function what the value of counter is, Matt, what would I have seen? I mean, unfortunately, the answer's right here on the board.

[00:07:18]
1 and then 2. And then followed by? 1 and 2. Is Matt right? Let's give him a hand, well done, Matt. Now, because we have two separate backpacks, one as a result of the first outer call and the first creation of add one and the first counter zero definition, one as a result of the next one, out it returns and we get our counter as 0. We run another function, we get it to 1. We run another function again, we get it to 2.

[00:07:47]
Now, what if this? What if this, what if I were instead to have defined, let me get this right, counter inside of add one? So it was add 1, now another function, now a new, oh sorry, yeah, now another function, now a new function, before I incremented, I defined counter is 0. What, Matt, would my values of counter be in my console if I had a counter is 0 defined in my add 1 function and then not above at?

[00:08:17]
Well, even if it were there above, because if it were in my local memory, where do we always check first, Joe, where do we always check first? The local memory. The local memory. So if I had counter is zero defining my local memory and I ran new func, new func, another function, another function, who wants to raise your hand and tell me what happens? Yeah, Ryan, go ahead. One, you get one every time, right?

[00:08:40]
Exactly, spot on, because we're going to, instead of going out to our backpack, even if it were technically defined there, we'd never reach it because JavaScript would first grab in local, increment it to one. That execution context runs, gone. Do it again, 1, right? Do it again. Okay, what if we didn't have it in outer or in add 1, but in global? Matt, what would our value of counter be after each call from 4 calls to new func, new func?

[00:09:16]
The first time would be 1, and then it's in global. The second time it would be incremented to 2, and the third time it would be incremented to 3, and the fourth time, 4. 1, 2, 3, 4. Well done to Matt. Alright. If you run outer again, store our returned add 1 function from it into another function, we get a brand new backpack because that add 1 function was created in a brand new execution context for the next running, the first running, the second running of outer.

[00:09:44]
We've got a brand new live counter is 0, brand new live counter is 0, and it was pulled out and stored on the back of another function. So when we ran another function twice, we were accessing the add one function that was created inside the next call to outer, that then got stored in another function.

Learn Straight from the Experts Who Shape the Modern Web

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