最近ECMAつながりでJavaScriptが気になって仕方ないです。 ExternalInterface APIの挙動というか実装が気なってあれこれ調べているのですが まだちょっとさわりしか触れていません。

ExternalInterfaceの実行されるタイミングが良く分からない

wonderflではExternalInterfaceが使えないので今回は使えませんが、検証例

navigateToURL(new URLRequest("javascript: alert('navigateToURL - second')"), "_self");
ExternalInterface.call("alert('ExternalInterface - first')");

下の行の方が先に実行されます。

 

 

ExternalInterface.callで返値が取れないこともある

リファレンスによると、再帰的な関数の返値が取れるかどうかは、ブラウザ依存ということらしいですが、 ExternalInterface.callにそのままJavaScriptのコードを渡した場合も取れないことがあるようです。 一般的に、navigateToURLとjavascript:の組み合わせの場合の方が、直感的に分かりやすい動きとなります。 ExternalInterface APIの方がブラウザ間の違いを吸収しているという風に書かれることもありますが、 僕自身ちょっとこのAPIの動きが今一掴めていないのでなんともいえません。

navigateToURL(new URLRequest("javascript: var bbb = function (x) {  alert('in bbb');return x * x;};"), "_self");
ExternalInterface.call("alert", "start"); // start
ExternalInterface.call("alert((function (x) {return x * x;})(17))"); // 289
ExternalInterface.call("function aaa () {alert('in aaa'); return x * x;}");
var ret = ExternalInterface.call("(function (x) {return x * x;})(18)") // 無名関数をその場で実行
ExternalInterface.call("alert", ret); // undefined

ExternalInterface.call("alert", "calling aaa");
var aaa = ExternalInterface.call("aaa",19); // 上で定義した関数。呼ばれる環境とそうでない環境もある
ExternalInterface.call("alert", "after calling aaa");

ExternalInterface.call("alert", aaa); // undefined いずれにせよ返値は取れない

ExternalInterface.call("alert", "end"); // end
ExternalInterface.call("alert", ExternalInterface.call("bbb", 100)); // 前述の通りこの時点ではnavigateToURLは評価されていないのでbbbは未定義

setTimeout(function () { // navigateToURLでjavascript:以降に定義した変数はあとでも使える
	ExternalInterface.call("alert", ExternalInterface.call("bbb", 100)); // 10000
	ExternalInterface.call("alert(bbb)"); // function
	navigateToURL(new URLRequest("javascript: alert(bbb(20))"), "_self"); // 400
}, 500);
 

ExternalInterface APIのシリアライゼーション

ExternalInterfaceではJavaScriptとActionScriptの橋渡しをするときに一旦データを文字列でXMLの形式にシリアライズするのですが、 このシリアライゼーションはシンプルな実装なので、循環参照しているオブジェクトを渡すと、スタックオーバーフローが起こります。 また、同時に比較的コストが掛かるデータのやり取りなので瞬時に大量のデータをやり取りするのには向いていないと言えます。

var a:Object = {};
a.a = a;
ExternalInterface.call("alert", a);
 

ExternalInterface今回はあんまり深く踏み込めませんでしたが、navigateToURLを使うと トップレベルにメソッドを生やすことが出来るということが分かりました。ちょっとだけ遊んでみましょう。

var scale:Number = 200;

ti.text = "x * x";

btn.addEventListener(MouseEvent.CLICK, function(e:MouseEvent):void {
	navigateToURL(new URLRequest("javascript: var func = function (x) {" + 
		"return " + ti.text + ";" 
	+ "}"), "_self");
	setTimeout(draw, 200);
});

btn.label = "draw";

function draw():void {
	var i:int = 1;
	
	graphics.clear();
	graphics.lineStyle(1, 0xffffff);

	graphics.moveTo(0, calc(0));
	while (i < stage.stageWidth) {
		graphics.lineTo(i, calc(i));
		++i;
	}
}
 

HTML5飯