Lesson Description
The "Multiple Closures Q&A" 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 spends a few minutes answering questions about closure and how the "backpack" is persisted in memory during subsequent function calls.
Transcript from the "Multiple Closures Q&A" Lesson
[00:00:00]
>> Will Sentance: Let's have thumbs on our ability to create a function, firstly new funk, then another function, that not only when run has its own local memory that gets thrown away each time it finishes running, but also a persistent private memory store of data on the back of the function definition that JavaScript goes to look in before giving up or before going to global, and instead looks first in the local of the function, new function, first in the local of function, another function, and then checks the respective backpack.
[00:00:37]
Let's have thumbs on that, and I look forward to seeing some clarifying thumbs. Ryan's got a clarification, Tenzin's got a clarification, Chris has got one, fantastic, Ryan, take it away. Um, so I guess the way I see it, it's like you called outer once, you're setting counter to 0, and then you call outer again, and the let is just resetting it to 0. Is that because the scope of the backpack changed, or because we reset it to 0, and maybe another way of thinking about it is like, what would you get if I console logged right before let counter.
[00:01:10]
Like what would you get when you call another function the first time? So another function, well, let's go through, so when I call outer the second time. You're like, was counter ever defined, like when you call outer the second time, was counter ever defined, or are you resetting it to. Great, great question, when we call outer for the next time, is it a new execution context? Yes, yeah, absolutely, in which we define counter as what?
[00:01:49]
0. Absolutely. In the first call to outer, we defined in its execution context, counter as zero, and we would normally, Ryan, have completely deleted that execution context, forgotten about counter is 0, but we didn't, because we returned out a function that held onto, doesn't really care about outer, it just held onto counter is 0 in its backpack. OK. When we then ran new funk, we still had access to that, but we had no memory of the previous running of outer.
[00:02:26]
Outer has no backpack, outer is just a regular old function. Outer is just running, creating counter zero inside, doing its normal thing, but then inside we defined a function that we then returned out and brought the live data from where add one was defined, from the first call of outer, out with it. Then, you know, nothing, well, yes, we used new funk twice, but to apply changes to the data in new funk's backpack, nothing to do with outer.
[00:03:00]
When we then ran outer again, just as any new function runs, it created a brand new local execution context where counter was set to what in the running of outer, Ryan, what's counter set to? Start at 0, to 0, set at 0, save it as 0. Yep. Literally store in the computer, counter is the number 0. And if we were to finish running outer without returning a function out of it, that would be forever forgotten.
[00:03:24]
But it didn't, because we returned out from outer a new saved version of that add one function that brought with it the surrounding data. And then, from then on, when we ran another function, another function has nothing to do with outer. It does though, persist the data from inside outer that was used by add one, that was going to be referenced by add one. And when another function runs, it still gets to hold onto that backpack of data that got dragged out.
[00:03:57]
But absolutely key to this is outer has no memory between its runnings. When new funk runs, its local execution context has no memory. We don't save anything anyway in it. Outer has absolutely no memory between its running, but it creates a function inside of it, one that does have a persistent memory. When it runs under the label new funk, twice, its local memory empties out each time, start again.
[00:04:30]
But it has this persistent memory. But be absolutely clear that new funk and another function are nothing to do with the running of outer, they're not kind of referring back to outer, they are instead new funk and another function, new funk and another function are the returned out add one function from the first running of outer, the returned out add one function from the second running of outer, but they didn't just get the function definitions, they got that counter is zero, live stored in a little mini memory attached to them.
[00:05:04]
That, you can see why the clarity around when a function gets returned out and the importance of realizing that the call to outer, the call to outer is not saving a relationship with outer in new funk is not saving a relationship with another function in new funk, it is not. It is instead saying go do the work of running outer. Whatever you return out, oh it's this function that was inside, known as add one, outside is going to be known as new funk, but also bring the live data from where that function was stored, and keep that on the back of that function, new funk.
[00:05:45]
In no way does new funk have any relationship ongoing to outer. There's not a resetting between, they have no relationship to it, pulled that data from that execution context out and holds it on new funk, and then this is gone. And the next time with the second running of outer, it pulls that data from its creation out onto another function, from the next running of outer, and then this is gone, and if you ran outer again, brand new execution context with a brand new counter is zero.
[00:06:14]
Just being very, very, very clear with yourself that another function has nothing to do with outer, besides that it got out the defined function from inside that happened to also have counter saved as zero attached onto it. It's, yeah, it's precise, it requires a level of precision that is quite surprising. Tenzin, you had a clarification. Yes, I just have a question on the memory allocation, so when the, does the memory allocated by the heaps, or sorry, the backpacks, do they get freed when the program ends or, oh, when the program ends, I mean, anything, you know, gets freed, but you would so when, as long as the label, another function and new funk remain, now if you were to reassign another function is set to null, at that point, at some point the garbage collection would happen and these would be deleted.
[00:07:13]
But otherwise, no, they are there, and that's a really smart point by Tenzin, because it's like, hold on. But I can't, or at least I'm not easily able to see, I can't go to another function.scope and see what's in there. I mean, I can use dev tools to see, actually, nowadays I can use the Chrome dev tools to see what's in a function's backpack, you know, down click it or whatever and down arrow and see what's in there, but it's certainly far from like explicit, and yet it could be a serious memory leak, meaning something which we've not necessarily tracked is still taking up space in our program.
[00:07:53]
So no, it's an interesting idea that we can have something that is able to very versatically have a huge amount of space potentially, you could put vast amounts of data in here that we are not explicitly referencing by global label, but is instead hidden on the function backpack. Hm, good question. But that's a very, it's a small point. We can see it in the dev tools and in practice, I think it's why it's very important to understand, and to be fair, to be fair, JavaScript does our optimization for us.
[00:08:32]
You would want, I hope, for that function, if you've got data in its backpack, it's because you've referred to it in this function, otherwise it gets deleted anyway, in which case you wouldn't want anything to be deleted in there. The only question really then becomes, are you surprised by how big the data is there? Well, you saved the function at where you declare the data, so hopefully you know.
[00:08:55]
But it is, it's not an actual risk of a memory, a memory leak would be explicitly you have data that can no longer be accessed and yet is still taking up space. This wouldn't be that because you definitionally can still access this data when you run another function, otherwise it won't be in here, because JavaScript would have explicitly not persisted it. So you can't have an inadvertent memory leak.
[00:09:22]
You could have a sort of colloquial, like, you know, a memory leak in the sense of you didn't realize that you've got a bunch of data in there, but that's a, you know, problem you could have anyway. Chris, you're up. Um, since the outer returns a function, you could just call it again with more parentheses, right? You wouldn't need to be, be precise. What you could call what again? You could just do something like outer open.
[00:09:52]
Oh yeah, yeah, for sure. Absolutely, and know that that's exactly how the module, traditional module patterns actually worked, because they allow you to immediately invoke and immediately create a memory space that is persistent and private for all your code that runs within potentially just a large module function. So yeah, that's exactly right, you, what Chris is sharing there is, wait, if I can return out a function that has state attached to it, data attached to it, um, could I maybe have a kind of initialization function that lays out all my state, for example, call it with a function inside that is going to have all my work to be done and know that that's sort of a wrapper function that I can ongoing use and that's essentially what the module pattern is doing.
Learn Straight from the Experts Who Shape the Modern Web
- 250+In-depth Courses
- Industry Leading Experts
- 24Learning Paths
- Live Interactive Workshops