ExternalInterface.addCallback で定義された関数は apply できない

ActionScript の関数を JavaScript から呼び出すときに、ExternalInterface.addCallback で関数を使うと便利なのですが、ここで公開された関数は apply, call, toString といったメソッドが定義されていません。

実験した ActionScript はこんなの。要は draw 関数を ExternalInterface を使って公開してます。

package
{
    import flash.display.*;
    import flash.external.ExternalInterface;

    public class ExtTest extends Sprite
    {
        public function ExtTest()
        {
            draw(0);
            ExternalInterface.addCallback("draw", draw);
        }

        private function draw(color:int):void
        {
            graphics.beginFill(color);
            graphics.drawCircle(0,0,50);
            graphics.endFill();
        }
    }
}

HTML は swfobject.js を使って楽チンに。

<script src="swfobject.js"></script>
<script>
window.onload = function(){
	var so = new SWFObject("ExtTest.swf", "ExtTest", "300", "300", "9", "white");
	so.write("box");
}
</script>
<div id="box"></div>

準備ができたところで、FireBug のコンソールでいろいろ実験してました。

# 関数は定義されている
>>> $("ExtTest").draw
function()

# 呼び出しもできる
>>> $("ExtTest").draw(0)

# でも、toString(), call(), apply() は失敗
>>> $("ExtTest").draw.toString()
$("ExtTest").draw.toString is not a function
>>> $("ExtTest").draw.call()
$("ExtTest").draw.call is not a function
>>> $("ExtTest").draw.apply()
$("ExtTest").draw.apply is not a function

apply, call, toString が定義されていないことが分かりますね。

toString と call はいいとして、apply がないのはちょっと不便です。


そこで、私は次のような実装ラッパー関数を作ってます。

function applySwf(swf, method, args){
    if(swf && typeof swf[method] == "function"){
        switch(args ? args.length : 0){
            case 0: return swf[method]();
            case 1: return swf[method](args[0]);
            case 2: return swf[method](args[0], args[1]);
            case 3: return swf[method](args[0], args[1], args[2]);
            case 4: return swf[method](args[1], args[1], args[2], args[3]);
            default: throw "argument too many";
        }
    }
}

ちょっとかっこ悪すぎな実装ですが、いちおう呼び出せます。

applySwf($("ExtTest"), "draw", [0xff0000])

SWF をロードした直後に、$("ExtTest").call = function(){...} としてやってもできそうなのですが、SWF 側で ExternalInterface.addCallback するタイミングは実装依存なので難しそうです。

追記

上のへぼ実装版 applySwf 関数をかっこよくする方法が 最速インターフェース研究会 :: Functionコンストラクタを使ってJavaScriptネイティブじゃない関数をラッピングする方法 にて紹介されています。ma.la さん、ありがとうございます。 (2007/02/16 23:00)