Flexでfor eachのなかでfunctionを作る落とし穴
というわけで、以前よりFlvプレーヤーを作っていたんですけども、ちょっと落とし穴にはまったのでメモ。
というわけでいきなり、検証用コード。
Asf.mxml
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init()"> <mx:Script source="AsfAction.as"/> <mx:TextArea id="log" /> </mx:Application>
AsfAction.as
private var added:Array; public function init():void { added = new Array(); var source:Array = ["test1", "test2", "test3"]; for each(var i:String in source) { added.push(function():void{ trace(i); }); } for each(var f:Function in added) { f(); } }
test.rb
added = [] ["test1", "test2", "test3"].each {|i| added.push(Proc.new { p i }) } added.each {|pr| pr.call }
つまり、いったんfor eachの中でfunctionを生成し、それをそのループの外で実行するというもの。
で、ActionScriptではまるのがこの部分。for eachの中でfunctionを生成した場合の挙動はこんな感じ。
ruby test1 test2 test3 ActionScript test3 test3 test3
見てのとおり、Rubyではfunctionが生成された時点での値を保持するのに対して、ActionScriptでは生成時に渡された変数名を保持しています。
ですので、値が「test1」のときに生成されたfunctionが実行されるときに初めて値を参照するため、「test3」が出力されてしまいます。
というわけで、ある個別の変数に値を入れることで回避することができます。
つまり別のクラスでいったん保持するということです。
というわけで、3つのファイルに分割しました。
Asf.mxml
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init()"> <mx:Script source="AsfAction.as"/> <mx:TextArea id="log" /> </mx:Application>
AsfAction.as
import Hoge; private var added:Array; public function init():void { added = new Array(); var source:Array = ["test1", "test2", "test3"]; for each(var i:String in source) { var hoge:Hoge = new Hoge(); hoge.strhoge = i; added.push(hoge); } for each(var f:Hoge in added) { f.call(log); } }
Hoge.as
package{ import mx.controls.*; public class Hoge { public var strhoge:String; public function call(log:TextArea):void { log.text += strhoge; } } }
で実行結果。
test1test2test3
めでたしめでたし。