2008年10月26日

イベント駆動なオブジェクトにする

本業がテストとドキュメント作成ばかりなので週末プログラミングがはかどります。
というわけで、コードをちょいちょいいじくっていたんですが、標題のライブラリが形になったので公開です。

ソース

色々試行錯誤した結果、

fladdict.net blog: イベント・ドリブンなJavaScriptのやり方
http://www.fladdict.net/blog-jp/archives/2005/06/javascript.php

上記サイトの丸パクリになりました。

特徴としては、
1.リスナー関数登録時に実行時のスコープを指定できる。
 (this参照先変わってめんどいから)
2.イベント名を配列で指定したら一斉登録できる。
 (successだろうがfailedだろうが同じリスナー関数呼んでよ!って時に便利)
3.オブジェクトの全メソッドに対して開始/終了イベントを自動付与できる。
 (メソッドの開始と終了時に勝手に呼ばれます)
4.イベントをオブジェクトのプロパティとして扱える。
 (文字列で指定するのはなんか嫌だって人向け)
W3C DOM3互換じゃないのが利点であり欠点。

詳しい解説は以下に(ソース内からの抜粋)

1. オブジェクトにイベント管理機能を付与する。
2. オブジェクトの全メソッドに処理開始/終了イベントを付与する。
3. イベントをプロパティとして使用する。

1. オブジェクトにイベント管理機能を付与する。
 付与方法:
  インスタンスに対して付与:
   EventDispatchable.includeTo(target);
  プロトタイプに対して付与:
   EventDispatchable.includeTo(target.prototype);
  主にプロトタイプオブジェクト設定後に使用される。
  対象のオブジェクトに新たに以下のプロパティが追加されるため、
  名前衝突注意。
   eventContainer
   addListener
   removeListener
   fireEvent
   getListenerCollection
   getEventNames

 使用方法:
  イベントの追加:
   target.addListener("valueChanged", func, obj);

   追加時の引数について:
    イベント名、リスナー関数、リスナー関数呼び出し時のスコープを引数にとる。

    イベント名(eventName):
     イベント名が配列であった場合、配列の全要素をイベント名として
     一斉登録する。
     target.addListener(["updateSuccess", "updateFailed"], func, obj);
     と
     target.addListener("updateSuccess", func, obj);
     target.addListener("updateFailed", func, obj);
     は同じ動作となる。

    リスナー関数(listener):
     function listenerSample(eventArgs) {
      alert(eventArgs.eventName);  // valueChanged
      alert(eventArgs.sender.name); // targetのnameプロパティ値
      alert(this.name);    // スコープのnameプロパティ値
     }
     リスナー関数が呼び出される時、イベント情報オブジェクトが渡される。
     これは、イベント発動時に設定されるが、基本情報として下記プロパティが
     与えられる。
      eventName: イベント名
      sender: イベント送信者

    スコープ(scope):
     スコープが指定されている場合、リスナー関数呼び出し時にオブジェクトのプロパティ
     として実行される。
     オブジェクトのメソッドをリスナー関数として指定した場合、関数内のthis
     参照先が変化すると困る場合に指定する。
     省略可。

  イベントの解除:
   target.removeListener("valueChanged", func, obj);
   登録時と同じリスナー関数、スコープであることが条件となるため、
   削除した場合はあらかじめリスナー関数、スコープをどこかに保持して
   おかなければならない。
   全解除した場合については保持の必要が無く、
   target.getListenerCollection("valueChanged").clear();
   と、リスナーコレクション経由で削除が行える。

  イベントの発動:
   target.fireEvent("valueChanged", {
    oldValue: hoge,
    newValue: fuga
   })
   イベントの追加削除が主に外部から使用されるのに対して、イベントの発動は
   自分自身が行う。
   イベント情報が指定されなかった場合でも、自動的にに空のオブジェクトが
   作成される。
   eventName、senderプロパティはfireEvent内で設定されるため、個別に設定する
   必要はない。
   独自に設定していた場合でも、上書きされるため注意。

2. オブジェクトの全メソッドに処理開始/終了イベントを付与する。
 概要:
  メソッドの実行前後に、開始イベント、終了イベントが発動する。
  付与前:
   実際のメソッド呼び出し
  付与後:
   開始イベント発動 → 実際のメソッド呼び出し → 終了イベント発動

 付与方法:
  インスタンスに対して付与:
   EventDispatchable.methodHook(target, "Start", "End", ["open", "close"]);
  プロトタイプに対して付与:
   EventDispatchable.methodHook(target.prototype, "Start", "End", ["open", "close"]);
  主にプロトタイプオブジェクト設定後に使用される。
  イベント発動時までには、EventDispatchable.includeToをオブジェクトに
  対して実行済みである必要がある。
  (付与時に実行済みである必要はない)

  付与時の引数について:
   startSuffix、endSuffix:
    startSuffix、endSuffixをそれぞれ開始/終了のイベント名称として使用する。
    メソッド名:hoge, startSuffix: "Fuga", endSuffix: "Piyo"の場合、
    開始イベント名:"hogeFuga", 終了イベント名:"hogePiyo"となる。
    省略された場合、それぞれ"Start"、"End"が既定値となる。
   
   ignoreProperties:
    開始/終了イベントを付与したくないメソッド名を配列で指定する。
    関数オブジェクト以外、既にフック済みのメソッド、
    EventDispatchable.methods内のメソッドについては、
    既定で付与対象外となる。

 処理開始/終了イベントのリスナー関数に渡されるイベント情報について:
  リスナー関数には、フック対象メソッドに渡されるargumentsオブジェクトが
  イベント情報内のargumentsプロパティとして設定されて渡される。
  フック対象メソッドが
   target.method = function(hoge, fuga) {};
  このような引数を指定しており、
  フック後にこのメソッドが
   target.method("引数1", "引数2");
  と呼び出された場合、methodStartイベントに登録された
  リスナー関数には(ややこしい)
   {
    sender: target,
    eventName: "methodStart",
    arguments: [
     "a",
     "b"
    ]
   }
  がイベント情報として渡される。
  argumentsオブジェクトには引数名がなく、扱いづらい場合は下記のような
  リスナー関数を定義すると良いかも。
   function listener(eventArgs) {
    method.apply(this, eventArgs.arguments);
    //フック対象と同じ引数の関数を定義してここに実際の処理を書く。
    function method(hoge, fuga) {
     alert(hoge); //argumentsプロパティが引数名に展開されて使いやすい。
     alert(fuga);
     alert(eventArgs.eventName); //イベント情報も普通に使える。
    }
   }

 付与前の関数オブジェクトの取得方法:
  フック前の関数オブジェクトは、フック後のメソッドのformerプロパティ内に
  保持されている。
  このため、フック前のメソッドに戻したい場合は、
  target.hoge = target.hoge.former;
  とすれば以前の状態に戻すことが可能。

3. イベントをプロパティとして使用する。
 概要:
  target.addListener("valueChanged", func, obj);
  といったイベント名文字列での操作ではなく、
  target.valueChanged.add(func, obj);
  とイベントプロパティとしての操作を可能にする。(両用可能)

 付与方法:
  インスタンスに対して付与:
   EventDispatchable.setEventProperties(target, ["valueChanged", "updateSuccess", "updateFailed"]);
  プロトタイプに対して付与:
   ※使用不可
  主にオブジェクトのコンストラクタ内で使用される。
  イベントプロパティ使用時までには、EventDispatchable.includeToを
  オブジェクトに対して実行済みである必要がある。
  (付与時に実行済みである必要はない)

 使用方法:
  追加:target.valueChanged.add(listener, scope);
  解除:target.valueChanged.remove(listener, scope);
  発動:target.valueChanged.fire({});
  全解除:target.valueChanged.clear();
  リスナー数取得:target.valueChanged.getLength();

  基本的に通常のイベント名文字列指定版からイベント名を取り除いた引数となる。
posted by 膳 at 16:22| Comment(0) | TrackBack(0) | JavaScript
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント: [必須入力]

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
この記事へのトラックバックURL
http://blog.sakura.ne.jp/tb/21886890

この記事へのトラックバック