我有一个小循环
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,在按下第二个按钮时跟踪等等?
答案 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());
}
}