This course has been updated! We now recommend you take the Deep JavaScript Foundations, v3 course.
Transcript from the "Closure Examples" Lesson
[00:00:00]
>> Kyle Simpson: Well this example is typically used to show how people gloss over the top of closure and don't fully understand it. It's not usually done with timer, usually it's done with, click handlers on buttons on a form or something like that. But the idea here is that we're thinking we're getting a new i for each iteration.
[00:00:16] And so when we close over that i on line 3, that it's gonna preserve the 1, 2, 3, 4, 5 value that it should have. So at the 1 second mark, we should get i 1, at the 2 second mark we should get i 2, i 3, i 4, i 5, that's the spirit of what we're trying to accomplish with this code, but it doesn't work.
[00:00:35] When you run this code, turns out it'll print 6, 6, 6, 6, 6, why? Well, there's two reasons why. There's a surface reason why, and there's a deeper reason why. The surface reason why is, well of course i is six at the end of the loop, and all these timers run a lot later, so of course it's gonna print out 6.
[00:00:56] But the deeper question is Why don't we get a new i for each iteration?
>> Kyle Simpson: Because we used the var, we made one i attached to the existing scope. Of course where all five of these inner functions are all gonna close over the exact same i, and reveal the exact same answer for i right?
[00:01:19] That's the deeper question. Something about the for loop tries to convince us that we're getting a new i with each iteration, but in this way we're not. Do you recall earlier in the course we talked about an example of how we could get a new variable for each iteration of a for loop?
[00:01:35] Do you remember the IIFE, here's the IIFE. Now we legitimately are creating a new i. So the i on line four that we're closing over, is that one, not the one in the for loop. We're making a whole new i for each iteration. So now when that setTimeout runs, what do you think it's gonna print?
[00:01:59] It's gonna print 1, 2, 3, 4, 5.
>> Kyle Simpson: So the IIFE was a way to create some scope, and not just some scope, but a variable each time we want it to run. But then we looked at block scoping, and we said that's cool, block scoping. I can make a new j by just saying let j = i.
[00:02:21] And now, when we close over j, we're closing over a different j each time.
>> Kyle Simpson: You see that?
>> Kyle Simpson: JavaScript has one more trick up its sleeve. We don't need even to do the whole let inside of the for loop thing. If you swap the var for the let, right there, it will automatically create a whole new i for each iteration.
[00:02:57]
>> Kyle Simpson: So, the question to critically ask yourself is, how many i's do I have? How many i's do we now have, we now have 5, one for each iteration. That's the only way this could possibly work is if each iteration got its own i, so that independently each one of those function closers, closed over a different i.
[00:03:18] If they all closed over the same one, you're gonna get the exact same output for all 5 console log statements.
>> Kyle Simpson: Yes?
>> Speaker 2: So each iteration in this situation consumes more memory cuz it keeps all the other i's in memory too, until setTimeout executes.
>> Kyle Simpson: I don't that I would say each iteration is creating more memory, each closure is more memory.
[00:03:43] Every time you create a closure there's a small tiny little sliver a few hundred byte worth of code, that's gonna be reserved for that closure. Closure does hold on to memory, and if you don't what it to, you don't create it or make sure to unset the closure when you're done.
[00:03:58] The second question, is closure and inner function or its ability to remember the variable outside of the inner function? A lot of people confuse this, a lot of people say the closure is the function. A function is not a closure, a closure is not a function. A closure is a characteristic of a function in a lexically scoped environment, which allows that function to continue to access variables outside of itself, even when that function is passed to a different location and executed.
[00:04:27] So, the most easy way for us to observe a closure is to have an inner function inside of an outer scope like an outer function. And then take that inner function and transport it out somewhere. Pass it as a callback, return it as a value, assign it to some global variable, set it to a timer.
[00:04:45] Whatever we do with it, if we take the inner function and we transport it out of the outer function, out of the outer scope, Now we have a function that's running around here, able to play with those variables from the inside. And the only way that can work is because closure.
[00:05:02] Isn't concept of closure contradicting lexical scope? No, they actually go hand in hand. If you have a lexically scoped system, which JavaScript is, and you have functions which are first class citizens, meaning they're values that can be passed around, which JavaScript does. If those two things are true of a language, which they are of JavaScript, you virtually have to have closure.
[00:05:25] Think about how crazy it would be if you took a function, and it referenced the variable, and it worked when you called it here. But then you passed it somewhere else and all of a sudden it forgot about its variable.
>> Kyle Simpson: That unpredictability would be rather frustrating, right?
[00:05:42] You virtually have to have closure to make functions as first class citizens work in a lexically scoped language. They are three pillars of the same building, you take one out, the building falls apart. So no absolutely closure is not a contradiction of lexical scope, closure is a logical conclusion of lexical scope.
[00:06:04] If you understand lexical scope, you now understand closure. And I would go so far as to say if you don't understand lexical scope, you have no hope of understanding closure.
>> Kyle Simpson: How do you unset a closure? You just get rid of the reference to the function that has as the closure.
[00:06:23] That could be unbinding an event handler, if that event handler function had the closure unbind it, and that'll throw away the reference to the function. When the function gets garbage collected, its closure gets garbage collected, which means any scopes it was holding onto can now be garbage collected.
[00:06:39] Exactly like you get rid of an array, you unset it, you set it to null, whatever, same principle. If a function has a closure, which just by virtue of creating a function, you're creating a closure over any scope it has access to. If a function has a closure and you wanna get rid of that closure, you've gotta get rid of that reference to that function.
[00:07:00]
>> Speaker 2: So in this case, they will unset automatically after those timeouts, right?
>> Kyle Simpson: Because the set timeout's gonna throw away those function references, exactly.