- dynamic/final/staticの情報はクラスのインスタンスからじゃないと取得できない
- staticメンバの情報はクラス自体からじゃないと取得できない
- クラスがインターフェイスかどうか分からない
- declaredBy属性が変数と定数には付かず、メソッドとアクセサにしか付かない
- 他のクラスを継承しているクラスだとネームスペースで修飾されたメンバが取得できない
- メソッドがfinal/overrideか分からない
- メソッドがrest引数を持つか分からない
と、非常に中途半端。
3番目のインターフェイスに関しては、factory要素に type="Object" の extendsClass 要素が含まれていないかで判断できることは出来るけど・・・。
なんにしろ、超使いにくいので正式リリースまでにはなんとかしてもらいたい。
困った。AS3では、クラスのリフレクションには flash.util.describeType メソッドを使えばOK!みたいな事が書いてあるけど、全然使えない。
何が使えないかというと、基本的に修飾子がpublicのものしか列挙してくれない。まあ、privateとかprotectedなメソッドはあまり使わないからいいのだろうけど、独自のネームスペースで修飾したメソッドを列挙してくれないのが痛い。クラス中、あるネームスペースで修飾されたメソッド一覧を取得する手段が無い訳です。
何か解決策は無いものか・・・
ついでに言えば、Java程度のリフレクション機構は欲しい。あと独自のアノテーション(メタデータ)の利用手段。
ActionScriptの言語仕様とかバグとかの理解度テストを作ってみました。全部出来たらかなり変態チックだと思います。
次の各コードを実行したとき出力される値を答えてください。勿論、Flash使わずに。
解答は次のエントリにポストします。
1.
var a:Number = 0; trace(a++); var b:Number = 0; trace(++b);
2.
var list:Array = [1,2,3,4,5]; var copy:Array = list; copy[0] = 6; trace(list); trace(copy);
3.
var o:Object = new Object();
o.a = 1;
o.f = function () : Void
{
trace(this.a);
}
var f:Function = o.f;
f();
4.
var a:String = 'hoge'; var b:String = 'fuga'; trace(a || b); trace(a && b);
5.
function f () : Boolean { return true; }
function f2 () : Boolean { return true; }
var a:Boolean;
var b:Boolean;
if ((a = f()) || (b = f2())) {
trace(a);
trace(b);
}
6.
function f (a:Number, b:Object) : Void
{
a = 1;
b.n = 2;
}
var n:Number = 0;
var o:Object = new Object();
o.n = 0;
f(n, o);
trace(n);
trace(o.n);
7.
var a:Number = 1;
{
var a:Number = 2;
trace(a);
}
trace(a);
8.
var o:Object = { a:0 };
var b:Object = {};
b.__proto__ = o;
b.a = 1;
trace(o.a);
9.
function f (g:Function) : Void
{
var b:Number = 2;
g();
}
var a:Number = 0;
var b:Number = 1;
f(function () : Void
{
trace(a);
trace(b);
});
10.
function f () : Void
{
var a:Number = 1;
}
function g () : Void
{
b = 2;
}
f();
trace(a);
g();
trace(b);
11.
for (var i:Number = 0; i < 3; ++i) {
function mul (n:Number) : Number
{
return n * 3;
}
trace(mul(i));
}
12.
trace((function(){ return 'hello'; })());
13.
var a:Number; trace((a = 4, 5, 6)); trace(a);
14.
trace((1).toString() == String(1)); trace((1.5).toString() == String(1.5)); trace((true).toString() == String(true)); trace((null).toString() == String(null)); trace((undefined).toString() == String(undefined));
15.
var a:Boolean = true;
if (a == True) {
trace('true');
}
else {
trace('false');
}
16.
trace((0xffff0000 | 0x0000ffff));
17.
class Hoge
{
public var a:Number = 1;
public var b:Number = 1;
public function Hoge ()
{
b = 2;
}
public function calc () : Number
{
return a + b;
}
}
var h:Hoge = new Hoge();
for (var prop:String in h) {
trace(prop);
}
18.
class List
{
private var list:Array = new Array();
public function getList () : Array { return list; }
public function dump () : Void { trace(list); }
}
var l:List = new List();
var l2:List = new List();
l.getList().push('a');
l2.getList().push('b');
l.dump();
l2.dump();
19.
class Movie
{
public function start () : Void { play(); }
public function play () : Void { trace('play!'); }
}
var m:Movie = new Movie();
m.start();
20.
var a:Number = 0;
function f () : Number { return a++; }
var list:Array = [1,2,3,4,5];
list[f()] += 1;
list[f()] += 1;
trace(list);
ActionScriptで次のように、ブロック内に関数を定義すると呼び出せない。
{
function hoge () : Void
{
trace("hoge");
}
}
hoge(); // 何も起こらない
一見、訳の分からないバグに見えるのだけど、実はECMAScriptの仕様書(A 文法要約(Grammar Summary)が分かりやすい)を見てみると、関数の定義が許されているのはSourceElement内のみで、Statement中では許されていないのです。
どういう事か簡単に説明すると、まず、ECMAScriptの文法定義は
- Program(最上位)はSourceElementsですよ
- SourceElementsはSourceElementの繰り返しですよ
- SourceElementはステートメント(Statement)か関数定義(FunctionDeclaration)の繰り返しですよ
となっていて、つまりはプログラムはステートメントと関数定義の繰り返しになるわけですが、ブロックの定義を見てみると、
- ブロック(Block)は'{'StatementList'}'ですよ
- StatementListはステートメント(Statement)の繰り返しですよ
と、SourceElementsではなく、StatementListになっているのです。
ステートメントには関数定義は含まれないので、ブロック中で関数定義するのは、なんと文法違反となるわけです。ちなみに、これはStatement及びStatementListが指定されている全ての場所に言える事で、ループであったりswitch-caseであったりif-elseにもあてはまります。
ただし、関数の本体だけは、
- 関数の本体(FunctionBody)はSourceElementsですよ
となっているので、関数内での関数定義はできることになっています。
と、いうワケで、実はFlashの動作はバグとは言い切れないんじゃないかなーと思ったりするんですが、そういう仕様ならちゃんとシンタックスエラーを出して欲しいです。シンタックスエラーが出ない辺りがなんとも微妙です(コンパイラはどんな処理をしているのか...)。
別に罠って程大した事でもないのですが、
class Hoge
{
private var foo:String = "foo";
public function getFoo () : String
{
return foo;
}
}
というクラスがあったとして、
var h:Hoge = new Hoge(); trace(h.getFoo());
これは勿論「foo」がtraceされます。
では、次のコードはどうでしょうか?
var h:Hoge = new Hoge(); var f:Function = h.getFoo; trace(f());
これはundefinedが返ってきます。
実は、このコードは先程のコードと同じように見えて、全然違うのです。このコードの場合、変数fにはメンバ関数への参照が代入されますが、hへの参照が代入されるわけではありません。つまり、この関数fはthisを知る手段が無いのです。そのため、getFoo内で参照しているメンバ変数fooは未定義となります。
長い名前のメンバ関数を何回か呼び出すときに上のように代入して使っていたり、関数の引数に関数オブジェクトとしてメンバ関数を渡す場合に、注意しましょうというお話でした。Function.apply()もしくはDelegateを使いませう。
Sound.positionはリアルタイム性のある値を返しません。
今日、見事にこれにハマった。Sound.positionをgetTimerのように同期に使ったりすると、ズレるズレる。
最初、原因が全く分からなくて、ふとSound.positionの値を毎フレームごとにトレースさせてみると、同じ値が3連続で返ってきてたりして泣きそうになった。
解決法としては、昨日書いたActionScript2.0 Game Frameworkにさり気なく入ってるASGF.TImer.Timerを利用して、
var music:Sound = new Sound();
var timer:Timer = new Timer();
music.loadSound('hoge.mp3');
timer.reset();
music.start();
timer.set(music.position);
としておいて、
var time:Number = timer.flush();
で経過時間を取得するとうまくいきます。



