您的位置:首页 > 其它

(AS3)在循环语句for、for each、while等语句中使用闭包需要注意的地方

2012-04-28 10:20 531 查看
遇到一个很有意思的BUG,是关于闭包的使用,大概简化后类似于以下代码:

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

猜一下上面代码的输出是什么?答案是:2 2

而事实上我期望的结果是:0 1

可为什么结果和我的期望相差这么大呢?

再看一段Lua代码:

arr = {}

for i=0,1 do
arr[#arr+1] = function()
print(i)
end
end

for _,v in ipairs(arr) do
v()
end

再猜一下,上面的答案?

这回的结果正好是我期望的结果,输出是:0 1

上面两段代码应该差不多是等价的,不过AS3中却并没有输出我期望的结果。

从我个人的期望上来说,我所理解的闭包,应该能在我定义function的时候就把所有的上下文保存好,这样也就能在调用的时候正确的取到upvalue,也就能正常的输出了。

对于这点,Lua的运行结果正是我需要的,可无奈的是我的主要开发语言是AS3,似乎AS3的闭包实现机制有些问题?循环语句中的 i 是一个引用,而再次调用 f 的时候,拿到的 i 还是原来的那个。

查了查资料,事实上早有人遇到过我类似的问题,传送门:http://stackoverflow.com/questions/422784/how-to-fix-closure-problem-in-actionscript-3-as3

所以,也就有了以下的解决方法,看代码:

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

这里做了件很巧妙的事情, test 方法返回了一个 function ,test 方法本身接受一个参数,而函数在传参过程中,类似 i:int 这样的基础类型数据是传值的,也就是说会拷贝一份 i 的复本出来,相同的数据类型还有其他的包括 Boolean 、Number 、String 、uint。

所以当调用 test 方法的时候实际上是保存了一个 i 的复本。然后 arr 再把 test 返回的方法塞进去,因此在调用 arr 中的方法的时候实际上调用的是 test 返回的那个匿名方法。

因此上面的输出就是我们期望的输出:0 1

这算不算一个BUG呢,不完整的闭包吗?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐