トップページ > 関数 > 無名関数を扱う上での注意



概要

無名関数とは
を先に参照してください。

前回でも説明したとおり、ActionScriptでは無名の関数を作ることが出来ます。


プログラム

var str:String = "ActionScript";
var f:Function = function():void { trace(str); };
f(); // ActionScript
 
ここで注目して欲しいのは2行目です。
この2行目が実行された段階では"ActionScript"が出力されるとは確定していません。


var str:String = "ActionScript";
var f:Function = function():void { trace(str); };
str = "Flash"
f(); // Flash
 
関数を実行する前にstrの中身を書き換えると、"ActionScript"ではなく"Flash"と出力されます。
このように、「実際に関数を実行した時点」でのstrの中身が出力されるわけです。


もう一つサンプルを見てみましょう。

var f:Function;
 
for (var i:int = 0; i < 5; i++)
{
	if (i == 3) f = function():void { trace(i); }
}
f();
 
5回ループする内、iが3のときだけ、iを出力する無名関数を代入します。
f()で実行してみると3ではなく5と出力されます。

これは先ほどのサンプルと同じ理由で、関数が実行された時点ではループは回りきっているのでiの値が5になっているからです。


対策

実際に実行される時点ではなく、無名関数を書いた時点での値を参照したい場合があります。
その対策方法を考えてみましょう。

別の変数を作る
for (var i:int = 0; i < 5; i++)
{
	if (i == 3)
	{
		var temp:int = i;
		f = function():void { trace(temp); }
	}
}
f(); // 3
 
特定時点での変数の中身を出力させたいなら、値を別の変数に保存するという方法があります。

高階関数
for (var i:int = 0; i < 5; i++)
{
	if (i == 3)
	{
		f = (function(i:int):Function { return function():void { trace(i); } } )(i);
	}
}
f(); // 3
 
無名関数を別の無名関数で囲い、引数を渡す形にするという方法もあります。
trace(i)はループ変数iではなく、新しく追加した外側の無名関数iを参照するので問題ありません。


ライブラリの場合

BetweenAS3やProgressionのSerialListで無名関数を扱う場合について解説します。
分からない方は飛ばしてください。

BetweenAS3
var t:ITween;
 
for (var i:int = 0; i < 5; i++)
{
	if (i == 3)
	{	
		t = BetweenAS3.func(function(i:int):void { trace(i); }, [i]);
	}
}
t.play(); // 3
 
BetweenAS3.funcで関数を指定できますが、第二引数でその関数の引数を渡すことが出来るので、そのままiを渡してコピーしてあげればいいです。
こうすれば、trace(i)のiはループ変数ではなく、引数で渡したiの方を参照することになります。

SerialList
var list:SerialList = new SerialList();
 
for (var i:int = 0; i < 5; i++)
{
	if (i == 3)
	{	
		list.addCommand(new Func(function(i:int):void { trace(i); }, [i]));
	}
}
list.execute(); // 3
 
SerialListでも同じように、Funcの第二引数にiを渡すだけです。


検証用コード

package
{
	import flash.display.Sprite;
	import jp.progression.commands.Func;
	import jp.progression.commands.lists.SerialList;
 
	public class Main extends Sprite
	{
		public function Main()
		{
			var list:SerialList = new SerialList();
 
			for (var i:int = 0; i < 5; i++)
			{
				if (i == 3)
				{	
					list.addCommand(new Func(function(i:int):void { trace(i); }, [i]));
				}
			}
			list.execute();
		}
	}
}
 

|新しいページ|検索|ページ一覧|RSS|@ウィキご利用ガイド | 管理者にお問合せ
|ログイン|