This course is out of date and does not reflect Frontend Masters current standards. We now recommend you take the JavaScript: The Hard Parts course.
Transcript from the "Counter Example 1" Lesson
[00:00:00]
>> [MUSIC]
[00:00:03]
>> Justin Meyer: Let's do this counter example. And this is one of my favorite examples here. Here's a counter function that returns a function itself, that when called, increments a variable and just returns that newly incremented value. We'll see what's going on in memory when JavaScript is doing this.
>> Justin Meyer: So the first thing again, JavaScript sees a function created in memory, sorry, a function defined and creates a function in memory.
[00:00:38] Now last time, we drew out that a function also has a prototype property, I'm not drawing that here just to save space, but there's one more thing that happens to a function whenever it's created. A function always has like this hidden magical reference to the call object in which it was created.
[00:00:56] So function always knows like, hey, I was created in the global call object. I should know about that call object, right? So there's a little magic pointer here that you can never reference, but the function internally knows.
>> Justin Meyer: So then we're gonna point the counter variable to that function, and then we're gonna go to the next line.
[00:01:19] Okay, so this line is gonna call the counter function. And again, whenever a function is invoked, a new call object is created that contains any arguments and any variables created, while that function is running. Now one other thing call objects have is they know about their parent function.
[00:01:41] They know about the function that called to create a call object, to create that call object. So it's kinda interesting. Functions, whenever a function is created inside of a call object that function knows about its parent call object. Whenever a function is called and creates a call object, well, that call object knows about its function.
[00:02:00] And this is important for how closures fundamentally work.
>> Justin Meyer: So,
>> Justin Meyer: Now this function's gonna be ran, so we created the call object, now we're gonna run the function. And we're gonna create the count variable. Well, variables always get put in their call object, the current call object that's running.
[00:02:21] So a count variable, is gonna be created inside this call object that's gonna point to the value 0.
>> Justin Meyer: Now we're gonna get to this line and again, we're creating a new function in memory. So I'm gonna create a function memory. And again, functions always know about their parent call object.
[00:02:42] And we're just creating a function in memory, we're not looking inside, we don't know what it's doing. But we are returning this function and sending its value as c1. So c1 is pointing to a function in memory, whom has a parent call object with a count variable.
>> Justin Meyer: When we run c1, and again, whenever a function is run a new call object is created and the body of that code is gonna be run.
[00:03:15] So this is where the c1 function's code is, it's gonna be running this count ++count. But it needs to find a count variable. So it's gonna first look for the count variable inside c1, this call object, not find it. And then walk up these kinda magic references, that I said these things that you can't actually access but JavaScript internally can access, to find the parent call object, look for a count there, find it, and increment it to 1.
[00:03:47] Now if it didn't find a count in this call object, it would just keep walking up to try to find a count variable. And eventually just say, hey, count is not defined. If it couldn't find it in the window, all right? So this is closures, really. This is how closures work.
[00:04:05] Essentially, functions know about their parent call object and call objects know about the function that was ran and it creates this kind of linking that JavaScript can go through to find variables.
>> Justin Meyer: So let me walk through doing this again, just to make sure you're getting it. So it'll return 1, because nothing now is referencing that call object, it will be garbage collected.
[00:04:37]
>> Justin Meyer: Then c1 is gonna be called again, a brand new call object is created. So this is important, every time a function is run a new call object is created, every time. So again, same process is gonna happen, this code is gonna be ran, c1's code is gonna be ran.
[00:04:53] It's gonna look for counts. It's gonna increment count to 2.
>> Justin Meyer: Is there a question?
>> Speaker 2: Yeah, within the closure is this.count the same as count?
>> Justin Meyer: No, we will talk about what this means in maybe the next set of slides. And, yeah, we'll get to this, I don't wanna get into it now.
[00:05:20]
>> Speaker 2: The answer is no, but we're gonna get to why.
>> Justin Meyer: Yeah, we'll get to why. But I will say that call objects themselves store the reference to this. In reality, if I was drawing this full call object out, it would have a this pointer that points to the window.
[00:05:40] It would have a this inside here and a pointer right back to the window. But we'll explain that later. Anyways, so we just called c1 again, and we just incremented our count variable to 2.
>> Justin Meyer: And now we're gonna call the counter function again. And this is really important, cuz a lot of times I think people don't, they kind of see code like this, and they might understand, there's two different count variables, but not really the mechanism that supports and allows that.
[00:06:12] So we're gonna understand what happens when you call counter again. When you ever call a function again, a new call object is created for that second invocation of counter and it's gonna know about its parent function. And we're gonna run the counter code now. So we're gonna create a count variable in this other call object, that's gonna be 0, and we're gonna return another function in memory.
[00:06:40] So a brand new function is gonna be created and we're gonna return that as c2.
>> Speaker 2: There's a question on how is that return function invoked. Is it internally invoked when called?
>> Justin Meyer: The return function, how does.
>> Speaker 2: The inner function, how is it invoked?
>> Justin Meyer: It's invoked as c1 right here and, or by calling c1.
[00:07:06] So we save, that inner function is returned. And we save it as c1 and then we're calling it here. And now, we're gonna create a new inner function. We're gonna actually create two functions in memory. This is really important where I think sometimes beginners might not understand, they think there's just one function created in memory.
[00:07:27] Actually, this function is created every time counter is created, as you can see here, we have two functions in memory. Or again, we're gonna save the function that was returned by the second calling of counter as c2.
>> Justin Meyer: And then we're going to call c2. Which will again call c2's code, which will look for a count variable.
[00:07:55] And this case, it's gonna try to find count here. Not find it, walk up these references and increment this count to 1 and then return 1 here.
>> Justin Meyer: Yeah.
>> Speaker 3: How does it work to have two return keywords in? Cuz once you hit that first return keyword, doesn't it break out of the function and then go to the code that called it, or is that because the second return is within that second function?
[00:08:23]
>> Justin Meyer: Exactly, so the big thing is to think of, functions are first class. You can do everything with a function that you can do with an object. In some ways, this return is almost like I returned an object from this outer function but in reality, I'm returning a function, so c1 is a function that you can then call.
[00:08:44]
>> Justin Meyer: So that's why this return, in here, nothing is happening with, until c1 is called.