Oh yeah last note before I start: closures are simple. Really, really simple, everyone who tells you they aren't are cheating you like when you were in school and everyone told you math is hard, true or not they were undermining you right from the jump. I'm not saying this to give you a confidence boost though, if you don't think they're simple you're more likely to think you don't understand them when you actually do; because they're simple. Ok, on with it already.
So I'm a visual thinker to start with and most things tend to conjure up an image for me, for a closure, it's a bag. A nice leather bag with a leather draw string you'd expect some hippy at a lord of the rings conference to have on his belt specifically. I think this is just because I associate closures with the concept of Greybeards due to LISP. Either way the image fits for me because if you found a leather bag of this nature that was closed, you could literally imagine anything being inside. Maybe it has some canadian coins, maybe it has a miniature time machine, a crossbow, and/or a beady eyed albino stretch-limo rat. Also it could be empty, or have another bag inside of it, more on that later.
Ok, so we have a funny looking bag with undetermined body of contents, what of it? Well somebody at one point made this bag, and somebody put stuff in this bag. Why? Well, if I had a bag like this that I was going to put stuff into, I would put things that work together. Let's imagine a different bag for a moment, a student's backpack. Inside you'll find topical books, notebooks, pens, and a calculator. These things all definitely work together. You can't operate the math book without the calculator (I too think this should be wrong but you've seen these as well as I have), or the paper without one of the pens etc so these have a purpose together as a whole. One could say you couldn't operate the school without this bag, we'll get there in a moment.
So now we have some extremely abstract concept, why? Well, it basically holds true to what a closure is. Now that's out of the way and you think I'm mental for wasting your time I'll actually show a closure. I'm going to use JavaScript because it's commonly understood and closures are fairly first class in it.
baggy = function() { alert('woo'); };
Ok, are you underwhelmed? Good. Now, don't think function is synonymous with closure. Sorry if that's what you just got from that. Here's what I hoped to show there: there is a bag(gy) with a little computation inside it which spits out a message box when evaluated. A message box machine, sounds like something you'd find in a Greybeard's little leather bag..
Now, I want to address a constant misconception about closures. I haven't talked about it yet and some people who think they understand closures or read other stuff about them think my explanation so far is pretty far off. Why is that? Scope. I haven't said anything about it at all. A lot of explanations and articles on closures are based completely on the fact that they seem to have strange behaviors in relation to scope. Defining a closure by the rules it follows regarding scope however is like defining an Apple by the count and organization of it's seeds. It's useful if all you care about is the seeds(scope) but won't really do if you want the whole apple.
Now that is out of the way, and I've made it over a page without giving any explanations you came for, I'll try to give you something useful.
(function() {
// Here is my current scope, look nothing interesting, so forget about scopes
someX = 40;
aClosure = function(x){
y = 2;
alert(x + y);
};
aClosure(someX);
}).call((function(){/* Here is my parent scope, also nothing so forget about scopes! */})());
Here we go, there's more code so it must be showing something more complex and detailed right? Eh, not much. aClosure is slightly more interesting than the last closure I showed you though, so back to the bag analogy what's in that bag? Well there's an opening in that bag that allows us to put things in it(the parameter), the value 2 held in a variable, and a computation that generates message boxes. Still pretty boring though, looks like a function, doesn't show anything we didn't already know. Come on, we wanted to see what this closure thing is we keep hearing all this hype about!
(function() {
// Here is my current scope, look nothing interesting, so forget about scopes
someX = 40;
aClosure = function(x){
y = 2;
alert(x + y);
};
bClosure = function(x) {
thisVarExistsInThisClosure = x;
aClosure(thisVarExistsInThisClosure);
}
bClosure(someX);
}).call((function(){/* Here is my parent scope, also nothing so forget about scopes! */})());
Now some of you read that and think "Well now you've got two closures and the code is getting contrived, will you just explain the bloody thing already??". Remember how I said they're a lot simpler than you think? That's what makes them difficult for people a lot of times, you see them often enough that you don't really recognize that it's anything special. Above you can see I have 2 bag's now. The same one from before, and a new one, bClosure. The new one has an opening for putting a variable into, and then it has a local variable to hold whatever you put in through that opening, also it has a pointer to our first closure. Wait, what was that last thing? If aClosure doesn't exist inside bClosure, why does bClosure have access to it? The answer to this is why people always think closures have something to do with scope.
Closures are bags, they have inside them three things:
- Variables, sometimes by value sometimes by reference but even a reference is a value where that value is a memory address.
- References to functions that may do something useful, the notebook in a students backpack is useless without a pen/pencil remember?
- Other bags which have another cluster of useful things in them that interact with each other to cause a result useful to the bag they're in. Think a student's backpack inside a school, it's one of many things in a school that are mutually beneficial when combined.
(function() {
// Here is my current scope, look nothing interesting, so forget about scopes
someX = 40;
aClosure = function(x){
y = 2;
alert(x + y);
};
bClosure = function() {
thisVarExistsInThisClosure = someX;
aClosure(thisVarExistsInThisClosure);
}
bClosure();
}).call((function(){/* Here is my parent scope, also nothing so forget about scopes! */})());
If you didn't notice the difference between this example and the previous one, note the number of parameters to bClosure. Why was I able to do this? If you had to write something that interpreted that and generated a bag based on it what would it generate? Semantically it is identical to the last example I gave, isn't it? Almost, there's a slight difference in that previously you had to put something into the opening of your bag at execution time before it would have a value for the local variable. So what is it doing under the covers now to fill in the local variable for bClosure since we aren't putting the value in through an opening?
Simple, when the bag is created, some things are put in it right away, namely anything it needs that's available at definition time, in our example someX. So whatever the Greybeard put in his leather bag that I found, it's all still in there, and though the bag has changed hands, I can still combine the things in it to get some pretty cool results. That last part is why everyone's obsessed with scope related to closures, if someone else gets your bag they can use your stuff because you put your stuff in the bag, so they're handy for sharing stuff.
If that clicked for you, you're wondering why I wrote this in such a contrived fashion? Closure's are an abstraction, and abstraction's compress things. To show you the same behavior without that abstraction meant less density, sorry about that, I like density too which is why I like closures. One last example now, though if you understood everything to this point you understand a closure where at definition time whatever it's owner has can be put into it for use later. Told you it was simple.
(function() {
// Here is my current scope, look nothing interesting, so forget about scopes
someX = 40;
aClosure = function(){
y = 2;
alert(this.someX + y);
};
aClosure();
}).call((function(){/* Here is my parent scope, also nothing so forget about scopes! */})());
Now here's the meat of when a lot of people run into closures, they're confused by the this keyword in JavaScript. This is simply a reference to the bag your current bag was created in. Remember how when your bag was created, you were able to put whatever you had around at the time in it? Well if your bag has "this" references, it just means you put some of the things lying around you in it before closing it up, and you want to use them. if someX were defined in aClosure in this example, this.someX would still reference the parents version. That's all their is to 'this', it just disambiguates between variables inside your closure and ones in the scope where your closure was defined (which were also placed inside your closure at definition time).
Now then, s/bag/closure/g and as always I would love to hear feedback. As I said at the start, I could be way off base with this, that's the problem in software, you could have a complete conceptual inaccuracy and be oblivious to it. We've all been there, or at least I have. Lots.
Cheers and I hope this helps someone.
Came across this post quite by chance. The best explanation I have read!
ReplyDelete