(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