今日はちょっと小休憩的な感じで書きたいと思います。
FlashのActionScriptをエミュレーションする中で
クロージャーのスコープ問題にぶつかりました。
JavaScriptの実装になれていればすんなり解決していたと思うのですが・・・
ActionScriptサンプル
import flash.display.MovieClip;
import flash.events.Event;
public class Main extends MovieClip
{
public function Main ()
{
super();
this.addEventListener(Event.ENTER_FRAME, this._onEnterFrame);
}
private function _onEnterFrame ()
{
trace(this);
}
}
_onEnterFrame
で出力される情報は
[object Main]
つまり、スコープはクラスに向いています。
これが、JavaScriptだと
new Main(); // <= [Window]が出力される
と、globalのwindowクラスに向いてしまいます。
当時、ここが全然理解できず結構苦戦しました。。。
JavaScript - MDN - Mozillaを徘徊してbind
を見つけました。
bind() メソッドは、呼び出された際に this キーワードに指定された値が設定される新しい関数を生成します。
この値は新しい関数が呼び出されたとき、一連の引数の前に置かれます。
ActionScriptをエミュレートするには、JavaScript側でthis._onEnterFrame
関数に対してbind(this)
をコールしてスコープを確約させる必要があります。
this.addEventListener(Event.ENTER_FRAME, this._onEnterFrame.bind(this));
解決してしまえば、それほど大きな問題ではないのですが
当時は結構苦戦しました。。。
また、この後、ActionScriptのaddEventListener
の仕様で
同一のスコープで、同一の関数は追加しない。っという仕様にぶつかったのですが
きっとJavaScriptだけで実装していれば関わらない仕様だと思うのでサラッと書きます。
ドキュメントにあるようにbind
関数をコールすると新しい関数
が生成されます。
const method = this._onEnterFrame.bind(this);
console.log(method === this._onEnterFrame); // false
既存の関数から新たにスコープが確約された関数が生成されます。
なので、元の関数と生成された関数は別物として扱われます。
こういった、ActionScriptとJavaScriptの挙動の違いを詰めていく調査と検証に大きな時間がかかってしまいました。
ただ、その副産物でJavaScript特有の仕様や機能など知る事もできたので
言語を理解するという点ではとても勉強になり
今でも仕事で役に立っているので結果オーライですw
明日は、WebWorkerに関して書こうと思います!