AS3: Main
First, before I even start to get into this, I want to say that Actionscript is an ECMAScript derivative. Javascript also falls into this category, so you can often find tutorials, examples, etc which translate easily from one language to another. A VERY good explanation of Closures and a few of their uses can be found here, but is biased towards Javascript, and isn't the most accessible thing out there ;)
Second, the uses of a "closure" are not really a clear-cut. If someone asked you "what can a class be used for?", there would be no single definite way to answer the question. Closures, in fact, can almost be thought of as another way of representing a class (but don't think of them that way!), but offer other practical uses as well. In this example, we'll discuss what a closure is and then use one to help solve the common problem of "passing arguments to event handlers", as well as pick up a nifty trick or two.
Background - Know this stuff first (if you don't, look it up before continuing)
Actionscript 3.0 is a garbage collected language. When you create an object, that object will exist only as long as there are references to it. When all references to the object are gone, the object can be deleted from memory. This is usually done using a technique known as "reference counting". We need to know that this happens, but not necessarily how it's done exactly.
Reference counting fails when you have two (or more) objects which point to each other, and no other objects which point to those. For this, you employ a technique from the graph theory world called "mark and sweep". We won't be too concerned with this corner case.
Finally, we all know that functions have a certain scope. That is, if you define a variable within a function "foo()", it only exists as long as the function does:
// No x exists here
function foo():void {
var x:int = 5;
// At this point we have a variable, x, with the value 5!
}
// But when we get here, it's gone again!
// Subsequent calls to foo() will re-declare a variable x and remove it when the function ends
And, last thing I swear, you have to realize that functions can be declared WITHIN other functions:
// This function should trace "Barred!!" ten times (untested)
function foo():void {
function bar():void { trace("Barred!!");
for(var i:uint=0;i<10;i++) bar();
}
Closures
So here's the wizardry that is a closure (untested):
var reference_to_bar:Function = foo();
function foo():Function {
var foo_string:String = "Foo's string";
function bar():void { trace(foo_string); }
return bar;
}
So here's the trick question of the hour. Given the above code, what would you expect to be traced to your console when you attempted to call reference_to_bar()? When you look at the foo(), it looks perfectly reasonable for foo_string to be accessible inside of bar(). The problem is, though, that it looks as if foo() has executed to completion, so foo_string shouldn't even exist anymore! So you run the code and get:
Foo's string
So what's going on here? It turns out that the function foo() actually "has" a reference to its inner function "bar()", and when you return that reference to the calling code and assign it to a variable, AS3 decides that it can't actually clean up the "foo()" function until all references to its inner "bar()" function have been released! Thus, foo_string continues to exist, and continues to be accessible to the bar() function.
Another very similar example could use an int variable to keep track of the number of times a function has been called, if you wanted to do that for some weird reason:
var reference_to_bar:Function = foo();
function foo():Function {
var count:int = 0;
function bar():void { trace("Times executed: " + ++count); }
return bar;
}
Each call to reference_to_bar() displays how many total times the function has been executed.
An example
So here's an example of a small game which uses closures. In addition to the last example given, the code also uses a closure to (sort of) solve the problem of passing parameters to event handlers, a question which comes up a lot and which is usually solved by throwing variables into a class or global scope when they don't really belong there (bad).
package {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.events.MouseEvent;
public class ClosureExample extends Sprite {
private static const SQUARE_WIDTH:int = 20;
public function ClosureExample()
{
// Create a square with some random color and place it randomly on the stage
var square:Bitmap = new Bitmap(new BitmapData(SQUARE_WIDTH, SQUARE_WIDTH, false, (0xFF << (Math.random() * 24))));
move_object_randomly(square);
addChild(square);
// Add our click event handler.
// NOTE: This *calls* the mouse_click_closure(...) function, which *returns* our actual handler!!
var handler:Function = mouse_click_closure(square);
// Add the returned handler. handler can be replaced with mouse_click_closure(square) if desired.
stage.addEventListener(MouseEvent.CLICK, handler, false, 0, true);
}
// Randomly repositions the passed in object somewhere within an arbitrary area of the stage.
private function move_object_randomly(object:DisplayObject):void
{
object.x = Math.random() * stage.stageWidth * 0.5;
object.y = Math.random() * stage.stageWidth * 0.5;
}
// Our closure!
private function mouse_click_closure(target:DisplayObject):Function
{
// Our mouse click handler now has access to whatever DisplayObject we pass in.
// You can pass in as much crap as you want in this way, and it will all be available to your event handler.
// Likewise, variables declared within this "outer" function will exist over the lifetime of the closure.
// score is used here to track the total number of times the user has clicked "target" DisplayObject.
var score:uint = 0;
// This is our "inner function" which will be returned. This function is assigned to a variable for verbosity
// reasons and can be returned directly if desired.
// THIS FUNCTION IS OUR ACTUAL EVENT HANDLER WHICH WILL BE CALLED EACH TIME THE TARGET IS HIT/CLCIKED
var mouse_handler:Function = function (e:MouseEvent):void {
// Test for a hit
if ((target.mouseX <= target.width) &&
(target.mouseY <= target.height) &&
(target.mouseX > 0) &&
(target.mouseY > 0))
{
// Increase the score and reposition the object!
trace("Hit #" + ++score);
move_object_randomly(target);
}
}
return mouse_handler;
} // End of mouse_click_closure(target:DisplayObject):Function
} // End of class ClosureExample
}
The preceding code draws a square somewhere on the stage. When the square is clicked, the score is incrementend and printed to the console, and the square is randomly repositioned. Also note that there are no class-level variables (besides a constant used for readability). The 'square' variable, for instance, exsists only within the constructor and within the closure.
Hopefully the comments can help you work through what exactly is happening. As a general rule of thumb, closures are created when you return references to functions which are declared within other functions. The outer function will continue to live, with all of its variables/inner functions "in-tact", until all references to its inner functions are removed.
Unforuntately I'm hitting the character limit, so if there are questions, just ask. There are other uses for cloures as well, so be sure to Google for those, as well as other introductions (for JS too! AS3 ones can be hard to come by).