On Fri, Jun 5, 2009 at 7:58 PM, Keith Hughitt<keith.hugh...@gmail.com> wrote:

> Could someone please explain to me how closures work in ActionScript? I am
> attempting to create a set of Flex Form fields, and assign an event-handler
> to each of them, however, it seems that after creation, all of the fields
> have the same event handler.

[...]

Let's take a simple example.

        var funcs:Array = [];
        for (var i:int = 0; i < 5; i++) {
            funcs.push(function ():void {
                    trace(i);
                });
        }
        for each (var f:Function in funcs) {
            f();
        }

First we push functions into an array. Then we iterate over the array
to call each function. What is the output?

5
5
5
5
5

The value of the variable i is 5. That's what gets printed when each
of the functions is called. It doesn't matter what the value was at
the time you set the function up. Ultimately when they are called, the
value of i is 5.

Now let's see how we can solve that. We want the functions to remember
state. Essentially we want a functor.

http://en.wikipedia.org/wiki/Function_object

Since Function is a dynamic class in ActionScript, you can add state
to a Function object without having to extend it.

Here now we modify the above example.

        var funcs:Array = [];
        for (var i:int = 0; i < 5; i++) {
            var f0:Function = function ():void {
                    trace(arguments.callee.index);
                };
            f0["index"] = i;

            funcs.push(f0);
        }
        for each (var f:Function in funcs) {
            f();
        }

f0 is a Function object that stores the index in an "index" property.
Later on when the function is called, we pull the value out of the
Function object for processing.

Here's the output:

0
1
2
3
4

If you're a purist, you can also create a class to do this.

class Functor {
    private var func:Function;
    private var state:Object;
    public function Functor(func:Function, state:Object) {
        this.func = func;
        this.state = state;
    }
    public function call(... args):* {
        return func.apply(state, args);
    }
}

And use it like this:

        var funcs:Array = [];
        for (var i:int = 0; i < 5; i++) {
            var functor:Functor = new Functor(function ():void {
                    trace(this.index);
                }, {index: i});

            funcs.push(functor.call);
        }
        for each (var f:Function in funcs) {
            f();
        }

Output:

0
1
2
3
4

Manish

Reply via email to