今まで少し数学的な話ばかり続きました。 今回はクロージャーや無名関数が展開されるタイミング、スコープについて ちょっと考えて見ます。 仕事でたまにAS2のレガシーなコードの保守をすることになるのですが、 AS2では良くonReleaseに無名関数function () {}を渡しますね。 今回はそのスコープについて考えようと思っています。

スコープの話

wonderflでコードの方も見てもわないと話が進まないのですが、 for文でiを回しながら、tfを子供にもつsp(Sprite)を生成して、

sp.addEventListener(MouseEvent.MOUSE_OVER, function (e:MouseEvent):void {
    tf.text = i.toString();
});
sp.addEventListener(MouseEvent.MOUSE_OUT, function (e:MouseEvent):void {
    tf.text = "";
});

とやります。勿論これは上手く行きません。 これはiやtfが評価されるのが、MouseOverがdispatchされたときなので、 for文は全部回りきって、tfは一番最後のspの中のtf、iは10になります。だから一番したの四角に10と表示されます。 オブジェクト指向で書けばいいじゃん!ってのは最もな話で基本的にこういう難しいロジックを組まないといけない状況 というのはそもそもデータ構造とアルゴリズムがおかしい場合が多いです。ですが今回はちょっと関数のスコープについて 考えてみるということで他のアプローチをしてみましょう。 ここでは評価のタイミングが問題となっているので、強制的にiやtfを評価させてしまえばそれでokです。 書き換えてみましょう

sp.addEventListener(MouseEvent.MOUSE_OVER, (function ($tf:TextField, $i:int):Function {
    return function (e:MouseEvent):void {
	    $tf.text = $i.toString();
	}
})(tf, i));
sp.addEventListener(MouseEvent.MOUSE_OUT, (function ($tf:TextField, $i:int):Function {
    return function (e:MouseEvent):void {
	    $tf.text = "";
	}
})(tf, i));

クロージャーのローカル変数に変数をコピーして保持します。

クロージャー

クロージャーというのは関数の一種で 環境(変数の値、スコープ)と処理をまとめて持ったオブジェクトのことです。 良くJavaScriptの本なんかに紹介される例を見てみましょう。

function counter():Function {
    var i:int = 0;
    return function():int { 
        i = i + 1;
        return i;
    };
}

var myCounter:Function = counter(); // iがココで初期化される
trace(myCounter()); // 1
trace(myCounter()); // 2
trace(myCounter()); // 3

counter()内のiはグローバルなスコープから見れませんがmyCounterを実行すれば 返り値としてその値が取得でき、カウント・アップされていきます。 counterは引数なしですが、先ほどはこれの引数としてループ内の一時的な値を与えること によって変数の値を保存しました。これを使えばちょっとしたオブジェクト指向的なことが できますね。

var newObj:Function = function ($x:Number) {
	return function ():Object {
		return {
			moveLeft: function () {--$x;},
			moveRight: function () {++$x;},
			getX: function () {return $x;}
		};
	};
};


var o0 = newObj(10)(); // newする
var o1 = newObj(20)(); // newする

for (var i:int = 0; i < 5; ++i) {
    trace(o0.getX(), o1.getX());
    o0.moveLeft();    // メソッドにアクセス
    o1.moveRight();   // メソッドにアクセス
}
//10 20
//9 21
//8 22
//7 23
//6 24

なんだか実用的ではありませんが、クラスをつくるってのはこんな感じなんでしょうか? ホントは関数の評価が実行時になされることを利用して遅延評価みたいなこともやりたかった のですが時間が無いので今回はここまでにします。それでは、また!

HTML5飯