2012-03-02 56 views
2

我有一个小环AS3封闭混乱

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,第二个按钮被按下时是否等于1?

回答

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传递给构造函数,那更好。

+0

为什么使用非类型化的VAR和非类型化的返回参数?它应该是'Function'和'void'。 – 2012-03-02 07:43:58

+0

我确定'arguments.callee'在AS3中被删除了,是吗? – alxx 2012-03-02 07:48:45

+0

@ValentinSimonov函数的返回类型实际上是未知的(取决于从外部传递的'func')。 'f'是无类型的,因为我们需要将'thisArg'和'func'粘贴到它上面。 – Manish 2012-03-02 15:13:40

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); 
... 

在GenericButton商店i和进入功能 - 这将导致匿名函数停止使用上下文变量i(循环计数器),并强制使用参数i

+0

这也是一个很好的解决方案。但是,它需要我更改GenericButton,因为上述解决方案不需要更改。这个解决方案的解释非常好。 – stats 2012-03-03 00:38:04

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()); 
     } 
    }