Check out a free preview of the full The Good Parts of JavaScript and the Web course

The "Closure Examples" Lesson is part of the full, The Good Parts of JavaScript and the Web course featured in this preview video. Here's what you'd learn in this lesson:

Doug walks through a few code examples on how to use closure. He begins with a global variable example and demonstrates how closure can eliminate the possibility of variable conflict and allow for more performant reusability.

Preview
Close

Transcript from the "Closure Examples" Lesson

[00:00:00]
>> [MUSIC]

[00:00:04]
>> Douglas Crockford: I need to demonstrate how this works because I can talk all day about a function and returns a function. But if we don't actually mess with it, it won't make any sense. So, let me start with a very simple example. We're gonna make a function called digit_name, digit_name will take a string or take a number and return a string.

[00:00:23]
Based on what that number is. And it will do that by returning an element from an array of strings. So you can see this is very trivial and unfortunately there's a problem with the way I wrote this. And that is I'm using a global variable called names. The problem with that is If there is anything else in my system that also has a global variable called names.

[00:00:48]
We're gonna get a conflict and either my functional fail or theirs will fail or maybe we both fail and that's bad. And there is no way you can test for that because you can't anticipate all of the code that your code is ever gonna have to run with.

[00:01:03]
And this is especially a problem in browsers if you have to run with ads, because ads come with some of the worst code you've ever seen, because they'll pay you to take their crap, right? And you can threaten them and say, we're not gonna take your ads unless you clean it up, and they'll say, okay, we'll get our money to someone else.

[00:01:21]
And you say, all right, just kidding, how it works and they use lots of global variables and eventually your gonna get the call. Your stuff stopped working because of some ad but it's not the ads fault it's your fault. So we need to reduce our dependence on global variables.

[00:01:42]
So here is another way that I could write this function. Here I've got the digit name function and it has a private variable called names, because all variables are private within the function scope. So if there is a global variable called names, no conflict, so that's all really good.

[00:01:58]
The bad part is that every time I call digit name, I'm going to have to construct a new array, put ten things in it just so I can take one thing out of it. That's hugely expensive. Now an optimizing compiler might observe that names as invariant over indications, and so it might try to optimize that out.

[00:02:19]
But optimizing compilers can take many minutes to do their work, and on the web we can't take many minutes. We have to start instantly, so the compilers are always gonna be really fast. Get going quick compilers, so they're not gonna do this so we need to do this.

[00:02:34]
So here's a third way to write this function. Here, I've got digit name, I've got a function with the private variable containing the names, but this time I'm returning a function. And the function I return is going to use the name. And I'm calling the outer function immediately, which means that what I'm going to be storing in the digit_name is not the outer function, but the return value of the outer function, which happens to be the inner function.

[00:03:05]
And that inner function, because of closure, will continue to have access to the names variable. Even after the green function has returned. This is the most important idea we're gonna talk about over these three days so I want to see everybody including you at home nodding, like yeah, okay, I got that.

[00:03:27]
Okay, you didn't get it, so let's do it again. So let's start over. This is the original one, okay? We've got the global variable. We've got our our simple digit name function. Now remember the thing we just talked about with tenant and his correspondence principle that we can take any value.

[00:03:48]
Wrap it in a function that will return that value, call that function immediately and it's the same thing, so I can do that in this case. I'm gonna assign to a digit_name the result of a function which will return a function that I call immediately. So you can see that this and this do exactly the same thing.

[00:04:10]
This one's just a little bit more code, okay? So everybody comfortable with that?
>> Speaker 2: Could you show those two?
>> Douglas Crockford: Yeah, so we're got to assign a value to the variable. We're going to assign the result of calling a function that will return that value in calling the function immediately.

[00:04:29]
>> Speaker 3: So in this in this example, is the main benefit of using this that we're preventing polluting the global stack, or what are the benefits?
>> Douglas Crockford: Well, that's what we'll get to, we haven't done that yet. So these two are doing exactly the same thing. So let's go have the global variable problem.

[00:04:44]
We haven't fixed anything yet. Getting setup. Now you're familiar with the idea that we can have local variables, declare a variable within a function that's local to the function. And we've got global variables which all the functions can see. But we can have, so that's two worlds, right, you can think of it as like the instance world and the static world.

[00:05:07]
It's not exactly like that but it's kinda like that, we've got two levels of visibility. But we can go much finer because every time we nest a function, we get a new place where we can keep stuff, another place where you can get variables. And each function will determine the visibility and the lifetime of the variables that are defined inside of it.

[00:05:28]
And so, it's not just local and global we can now have in between scopes and and they're just scopes. So I'm gonna make one change to this one. I'm gonna take this global variable and I'm going to just move it down one line to be inside of the outer function, okay?

[00:05:49]
So we've introduced a third place where we can keep stuff which is now in the scope of the outer function. This is the same result that we had before. Okay, so we've got digit name which will receive a value which is a function which is obtained from a function that we call immediately.

[00:06:13]
And that inner function will continue to have access to the names variable of the outer function.
>> Douglas Crockford: So this is closure. This is the big idea, the key idea in functional programming. And this is the good stuff. This is the good part. We're gonna spend a lot of time tomorrow playing with this model.

[00:06:38]
Any questions before we go on to the next one, yeah?
>> Speaker 4: So, you solved the global variable problem by moving it in there but you said the solution to this was that basically you move the invocation, move everything into a heap? Great, so now in heap are you going to have three instances of the names array?

[00:06:57]
If you call alert digit name three then four then five.
>> Douglas Crockford: No, so that's a really good question. So you're familiar with the idea of an activation record in a programming language. So the activation record will contain the return address. Contain all the input parameters and might also contain local arguments and her local variables and stuff like that.

[00:07:23]
So, in JavaScript, or in a functional language like this, the activation, a function object will contain a pointer to the code. So it knows what to execute when the function is invoked, and the function object will contain a reference to the activation of the function that created it.

[00:07:45]
So this case, this function or the object that is created by that function expression will be linked to the context of the green function that created it. So through that activation connection it will have access to the names variable not to the value of the names variable that it was created with but the actual names variable itself.

[00:08:13]
It will always have access to that and will continue to have it as long as it survives. If at some point I said digit name to undefined at that point all of that stuff can get garbage collected. But until then, as long as the inner function survives and needs that activation, it will keep it and the garbage collector will not touch it.

[00:08:39]
So when we call digit name, we'll be calling this function which will simply go to the array and return a value. So we can call it with one, two, three, and we're just calling it and returning three values. We're not creating any stuff because we're just calling that little function.

[00:08:58]
>> Douglas Crockford: So let me show you a slightly more complex example. This is the fade function. The fade function is something we might run in a browser which will pass in the ID of a DOM element. And it will change the colors of that element from yellow to white gradually over a couple seconds and so, here is that the fade function.

[00:09:25]
First thing it will do is, it will use document that get element by ID to look up the ID and obtain a dominant element. And it will create a variable called level and it will set it to 1. It will create a function called step and it will then call step timeout which will call step in 100 milliseconds, and then it returns.

[00:09:47]
So at this point, the fade function has finished its return, it's done. Suddenly, 100 milliseconds later the step function runs, so the step function will go to its level variable. The actual level variable of the parent function, and turn it into a hex character. It will go to the DOM element in change its background color, it will go to the level variable again.

[00:10:14]
If it's less than fifteen which will be because it was one we will add one to it. We're now changing the value of the variable in the outer function. And we'll call set timeout to do this again and we'll keep doing it until eventually level gets up to 15 and then we stop.

[00:10:33]
And at that point, the garbage collector can come in and take it all out.
>> Speaker 5: So you better call you would just say, fade 75, times you don't have to say, var X equals fade 75, you just fade 75.
>> Douglas Crockford: Will be probably give it a name.
>> Speaker 5: Will be garbage collector if we don't even name?

[00:11:08]
>> Douglas Crockford: No, I mean I mean that the argument to fade will probably because we're talking to a DOM and certainly has been heard to be the strings [INAUDIBLE] yeah, but yeah, so we can do that. So suppose there are two DOM elements and we want to feed them both simultaneously.

[00:11:24]
So we call feed a fade be immediately. Will there be any conflict between them? And the answer is no because every time we call fade, we get a new DOM variable, we get a new level variable and a new step function. And they will not interfere with each other they're all within their own function scopes and will be completely unique.

[00:11:45]
So one of the nice things about this construction is that we get that separation. So that we can have lots of complex behavior going on without those behaviors interfering with each other.
>> Speaker 5: Each time you call fade start time out passes a new instance of the step function to DOM, is that correct?

[00:12:12]
And so, the DOM doesn't encourage collect those step functions until after its invoke the timeout after it's called out function.
>> Douglas Crockford: The DOM is only involved in that with column, we're looking at the style to change the color. Otherwise, the dom is not aware of that any-
>> Speaker 5: I'm sorry, setTimeout is-

[00:12:35]
>> Douglas Crockford: Yeah, the setTimeout is, doesn't actually belong to JavaScript, it does belong to the browser.
>> Speaker 5: Browser.
>> Douglas Crockford: Also has it. Yeah, so each time we call fade, we will make a new step function and we'll pass that step function many times to set timeout until we finish.

[00:12:58]
>> Speaker 5: I was just trying to get my head around why step doesn't get garbage collected. It's because the browser.
>> Douglas Crockford: Right, cuz the browser's holding it in its timer queue.
>> Speaker 5: Okay, its timer queue.
>> Douglas Crockford: And and as long as anything in the system is aware of something that's rooted in the garbage collector won't touch it.

[00:13:15]
>> Speaker 5: Okay, thanks.
>> Douglas Crockford: Yes.
>> Speaker 6: What's the initial value of h when you declared that?
>> Douglas Crockford: First time H will be the string one.
>> Speaker 6: And then what does the two string 16 do?
>> Douglas Crockford: 16, toString 16 means make a hex character, so this will give us from one to F.

[00:13:37]
And then, we add them to here so the file color will be pure white, so it'll be FFFFF

Learn Straight from the Experts Who Shape the Modern Web

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