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


遇到一个很有意思的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呢,不完整的闭包吗? 

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM