Normally, local variables die after functions are through running:
function someFunction() : void
{
var myFriend : String = "Fred";
}
someFunction();
trace( myFriend ); //1120: Access of undefined property myFriend.
But things get wacky when functions return other functions:
function functionMaker() : Function
{
return function() : void { trace ( "I am a new function!" ) }
}
In the above example, I haven't used any variables. I'll add one shortly. For now, just note that functionMaker's return type is Function and that it, in fact, does return a function. The function it returns in anonymous. That is, it doesn't have a name. We can access it this way:
var aNewFunction : Function = functionMaker();
aNewFunction(); //I am a new function!
functionMaker returns a new (anonymous) function, which we stored in a variable called aNewFunction. We then called the function stored in aNewFunction by following the variable's name with the call operator: ().
You can shorten the above to...
functionMaker()();
That looks odd, but it works. The first call operator calls functionMaker; the second class the function that functionMaker returns.
Let's take another look at the guts of functionMaker:
function functionMaker() : Function
{
return function() : void { trace ( "I am a new function!" ) }
}
From now on, in the case of functions that return other functions, I'm going to call the main function the outer function and the returned function the inner function:
//outer function
function functionMaker() : Function
{
//inner function
return function() : void { trace ( "I am a new function!" ) }
}
What happens if we create a local variable in the outer function and try to reference it from the main body of our code?
function functionMaker() : Function
{
var food : String = "cheese"?
return function() : void { trace ( "I am a new function!" ) }
}
functionMaker();
trace( food ); //1120: Access of undefined property food.
No surprise. food is a local variable to functionMaker. We can't access it from outside the function. However, what if we try to access it from within the inner function?
function functionMaker() : Function
{
var food : String = "cheese"?
return function() : void { trace ( "I am a new function, and I love " + food + "!" ) }
}
var aNewFunction : Function = functionMaker();
aNewFunction(); //I am a new function, and I love cheese!
Take a moment to think about what just happened. When we ran the inner function, it spit out the word "cheese" as part of its output, even though that word wasn't stored inside it. It got that word by referencing the outer function's variable. So inner functions have references to the variables in their parent functions.
Now take a closer look at these lines:
1. var aNewFunction : Function = functionMaker();
2. aNewFunction(); //I am a new function, and I love cheese!
functionMaker, the function that contains the value "cheese" finishes running on line one, so under normal circumstances, cheese should be dead by line two. But it's not. And that, kids, is a closure: inner functions have access to the variables in their parent, outer functions even after those outer functions have stopped running!
Not only can they access those variables, they can alter them!
function functionMaker() : Function
{
var food : String = "cheese"?
return function() : void { food += " crackers"; trace( "I am a new function, and I love " + food + "!" ) }
}
var aNewFunction : Function = functionMaker();
aNewFunction(); //I am a new function, and I love cheese crackers!
There's one last key aspect of closers I haven't explained: From the inner-function's point-of-view, the variable is static. That is, it keeps its value even after the inner function is done running! That means that each time you call the inner function, it can access the last value stores in the variable.
function counterMaker() : Function
{
var count : int = 0;
return function() : void { trace( count ++ ) }
}
var aNewCounter = counterMaker();
aNewCounter(); //0
aNewCounter(); //1
aNewCounter(); //2
aNewCounter(); //3
Compare this with a stand-alone version of the inner function:
function counter() : void
{
var count : int = 0;
trace( count++ );
}
counter(); //0
counter(); //0
counter(); //0
counter(); //0
In the stand-alone version, the variable counter gets reset to zero each time the function is called. It gets incremented in the trace, but by then it's too late. The function is over and its local variable dies. We can use closures to add static variables to functions!
As I said at the top of this article, I had read about closers for years, but I didn't see how they were useful in Actionsctipt, which is an object-oriented language. As such, if I wanted a counter with a static variable, I would make and use one like this:
public class Counter
{
private var _count : int = 0;
public function next() : { trace( _count++ ) }
}
var counter : Counter = new Counter();
counter.next(); //0
counter.next(); //1
counter.next(); //2
counter.next(); //3
So what made me suddenly want to use a closure? Well, I was trying to do something very specific involving regular expressions. I started with this text:
Photo is of a sunset.
Photo is of my sister.
Photo is of a horse.
Photo is of a birthday party.
... [many other similar lines]
Photo is of a calm lake near the city.
From that, I wanted to output...
Photo1 is of a sunset.
Photo2 is of my sister.
Photo3 is of a horse.
Photo4 is of a birthday party.
... [many other similar lines]
Photo[n] is of a calm lake near the city.
...with each new "Photo" getting an incremented number appended to it.
To start out with a simpler example, let's say that I didn't care about the numbers incrementing. I just want a random number appended to the end of each "Photo" (Photo2, Photo9, Photo3, etc.).
var text : String = "";
text += "Photo is of a sunset.";
text += "Photo is of my sister.";
text += "Photo is of a horse.";
text += "Photo is of a birthday party.";
text += "Photo is of a calm lake near the city.";
var regExp : RegExp = new RegExp( "Photo", "g" );
text = text.replace( regExp, String( Math.round( Math.random() * 10 ) );
trace( text );
That doesn't get me exactly what I want. It doesn't append a random number onto the end of each "Photo". Instead, it replaces each "Photo" with a random number:
6 is of a sunset.
0 is of my sister.
10 is of a horse.
6 is of a birthday party.
3 is of a calm lake near the city.
I could prepend the original string "Photo" onto the random number ...
text = text.replace( regExp, "Photo" + Math.round( Math.random() * 10 );
... but that second parameter is starting to get long, ugly and unreadable. Luckily, the String.replace() method accepts a function as its second parameter:
function appendRandomNumber() : String
{
return arguments[ 0 ] + Math.round( Math.random() * 10 );
}
var regExp : RegExp = new RegExp( "Photo", "g" );
text = text.replace( regExp, appendRandomNumber );
trace( text );
What happens here is that replace calls the appendRandomNumber method, handing it an array, which the function receives in a variable called arguments. If you want to know everything that's stored in the array, look up String.replace() in help. All we care about here is arguments[0], which contains whatever the regular expression matched: in this case, the word "Photo." The function returns that plus a random number:
Photo4 is of a sunset.
Photo7 is of my sister.
Photo0 is of a horse.
Photo10 is of a birthday party.
Photo6 is of a calm lake near the city.
Putting that idea together with closures, we can hand String.replace() an inner function that uses its parent's static variable to keep count:
function counterAppenderMaker() : Function
{
var counter : int = 1;
return function() : String { return arguments[0] + counter++ }
}
var regExp : RegExp = new RegExp( "Photo", "g" );
text = text.replace( regExp, counterAppenderMaker() );
trace( text );
Which yields this output:
Photo1 is of a sunset.
Photo2 is of my sister.
Photo3 is of a horse.
Photo4 is of a birthday party.
Photo5 is of a calm lake near the city.
I think this is an elegant solution. To be fair to OOP, there is another way of doing this:
public class AppendCounter
{
private var _counter : int = 1;
public function doIt() : String
{
return return arguments[0] + _counter++;
}
}
var regExp : RegExp = new RegExp( "Photo", "g" );
var appendCounter : AppendCounter = new AppendCounter();
text = text.replace( regExp, appendCounter.doIt );
trace( text );
I am not advocating closures over objects (or vice versa), but it's nice to know the closure method. Depending on what I'm trying to achieve, it may be clearer to have all the logic in one class rather than referencing a second class (or a global variable) just to increment a counter.