2008年01月30日
AS3 で関数かつクラスっぽいオブジェクトを作る
jQuery では、$ を関数としてもハッシュとしても使っている。
- $("#foo") → エレメントを選択
- $.each() → ユーティリティ関数の呼び出し
as3Query でも同じようにやりたかったんだけど、いい方法が思いつかずに苦しんでた。
失敗例 1
$ を public な function として定義する。
Function は dynamic なクラスなので、$["each"] = function(){} とすれば関数を代入できる。$["each"] で呼び出せる。
けども、$.each とすると、静的型付け言語の宿命でコンパイルエラーになってしまう。
失敗例 2
$ をクラスにしてみたら、$() がキャストと解釈されて、$("#foo") が実行時のキャストエラーになった…。
失敗例 3
Function を extends しようとしたら、final だって怒られた。
失敗例 4
Proxy クラスを使ってみたけども、キャスト時の挙動は決められないようだ。
ひらめいた
Object にしちゃえばよかったんだ!
package { public const $:Object = function():void { if(!$.each){ $.each = function():void{ trace("each called"); } } trace("$ called"); } }
Object なので $.each のチェックは実行時。よって、コンパイルが通る。
実行結果はこんな具合。
$(); // $ called $.each(); // each called
ただ、これだと $() を呼んだあとじゃないと、$.each が使えない。
ちょっと泥臭く対応したらこうなった。
package { public const $:Object = function():Function { var f:Function = function():void{ trace("$ called"); } f.each = function():void{ trace("each called"); } return f; }(); }
無名関数の中で、のちほど $ になる関数 f を宣言しておいて、each を代入している。
$ 自体は public なオブジェクトなのでプログラムの最初のほうで初期化される。初期化時に無名関数が評価されて、each が設定された f が $ に代入されるというわけだ。
実行結果はこんな具合。
$.each(); // each called $(); // $ called
感想
なんだか JavaScript ちっくで楽しい。同じようにすれば、JavaScript 風のクラス定義もできるような気がする。