AS3关闭混乱

时间:2012-03-02 03:54:14

标签: actionscript-3 closures

我有一个小循环

var a:Array = [{name:Test1},{name:Test2},{name:Test3},{name:Test4}]
var b:GenericButton; //A pretty basic button component

for(var i:int = 0; i < a.length; i++){
  b = new GenericButton(a[i].name, function():void { trace(i) });
  this.addChild(b);
}

按下按钮时,将执行提供给GenericButton的功能。

我遇到的问题是,无论什么按钮,我按下4的值(数组的长度)总是输出。

我如何确保在按下第一个按钮时跟踪0,在按下第二个按钮时跟踪等等?

3 个答案:

答案 0 :(得分:6)

嗯,你可以这样做:

var f:* = function():void { trace(arguments.callee.index) };
f.index = i;
b = new GenericButton(a[i].name, f);

更好的是:

function createDelegate(obj:Object, func:Function):Function
{
    var f:* = function ():* {
        var thisArg:* = arguments.callee.thisArg;
        var func:* = arguments.callee.func;

        return func.apply(thisArg, arguments);
    };

    f.thisArg = obj;
    f.func = func;

    return f;
}

...

for (...) {
      b = new GenericButton(a[i].name,
          createDelegate({index: i}, function():void { trace(this.index) }));
}

在某些(大多数?)情况下,如果您创建了一个单独的类并将i传递给构造函数,那会更好。

答案 1 :(得分:1)

这是使用闭包时最基本的错误。您可能认为在创建GenericButton时设置了i。但是闭包只是直接链接到变量i并在调用匿名函数时使用此链接。到这时,循环结束,所有到i的链接都指向同一个整数,其值为4 要解决此问题,只需以某种方式传递i的值 - 例如,作为GenericButton构造函数的附加参数。在这种情况下,将在每一步创建i的副本,其值为0,1,2,3 - 就像您需要的那样。

...
b = new GenericButton(a[i].name, function(i:int):void { trace(i); }, i);
...

i存储在GenericButton中并传入函数 - 这会导致匿名函数停止使用上下文变量i(循环计数器)并强制它使用参数i

答案 2 :(得分:1)

创建一个返回函数的函数。这是演示它的FlexUnit测试方法。

    [Test]
    public function closureWin():void
    {
        var functions:Array = [];
        var mkFn:Function = function(value:int):Function
        {
            return function():int
            {
                return value;
            }
        }

        var i:int;
        for (i = 0; i < 10; i++)
        {
            functions.push(mkFn(i));
        }

        var j:int;
        for(j = 0; j < 10; j++)
        {
            assertEquals(j, functions[j]());
        }
    }   

这是一种展示您所看到的行为的测试方法:

    [Test]
    public function closureFail():void
    {
        // basically to see if this works the same way in as3 as it does in javascript
        // I expect that all the functions will return 10

        var i:int;
        var functions:Array = [];
        for (i = 0; i < 10; i++)
        {
            functions.push(function():int{return i});
        }

        var j:int;
        for each (var f:Function in functions)
        {
            assertEquals(10, f());
        }
    }