2014年1月11日土曜日

JavaScriptによる関数のラッピング

目的

既存のWebアプリケーションにおいて、可能な限り既存の処理を変更せず関数の呼び出し前後に処理を入れたいことがある時の方法を考えてみた。

適用例

この方法の適用例としては以下のような状況が考えられる。

  • デバックやトレース情報の採取や表示を行う。
  • フォームのonSubmitやonCheckなどのイベントに既に関数を割り当ているときに処理を追加する。
実装方法

JavaScriptには、関数の this 値を指定のオブジェクトで置き換え、関数の引数を配列を置き換えて関数を呼び出詩を行うapplyメソッドという関数が用意されているのでこれを利用し実装する。
実際のコードとしては、既存の関数の前処理、後処理を実装した関数(functionWrapper)を用意し、既存関数の呼び出し部分を変更することにより、functionWrapper内に定義された処理を元の関数の呼び出し前後に処理を挟み込むことができる。
/**
 * 引数で指定した関数をラッピングしての前後に
 * 処理を差し込みます。
 * @param functionName 関数名
 * @return {Boolean}
 */
function functionWrapper(functionName){

    //前処理
    ~

    //既存ロジックの呼び出し
    var tmpArguments = 
           new Array(arguments.length-1);
    for(i = 0;i < arguments.length-1;i++){
        tmpArguments[i] = arguments[i+1];
    }
    var retValue = window[functionName]
                     .apply(window,tmpArguments);

    //後処理
    ~

    return retValue;
}

既存関数の呼び出しとして、例えば以下のようにformのonSubmitに既存のフォームのチェックをするcheckFormという関数の前後になんらかの処理を入れる場合、もともと定義されているchekFormという関数を文字列としてfunctionWrapperの第一引数とし、もともとcheckFormに指定されていた引数をそのままfunctionWrapperの第二引数以降に指定し、functionWrapperの呼び出しに書き換える。 (関数呼び出しの際の引数の受け渡しは、JavaScriptではオーバーロードがサポートされていないため同じ関数名は同一関数が実行されすべての引数がわたされるという性質を利用している。) この書き換えによりfunctionWrapper内に定義された処理を元の関数の呼び出し前後に処理が差し込まれる。
<form method="GET" action="action.do" 
  onSubmit="return checkForm(this);" >
<input text="name" value="value" />
<input text="submit" />
</form>

↓

<form method="GET" action="action.do"
  onSubmit=
    "return functionWrapper('checkForm',this);">
<input text="name" value="value" />
<input text="submit" />
</form>


※2014/02/09 ソースコードの表示方法を変えました。