JavaScript おれおれ Advent Calendar 2011 – 17日目

.apply()成り済ましができたり仕事をスルーして丸投げしたりする事ができますが、さらにもうひとつ、フックを行う事ができます。

フック(hook)というのはフック船長のフック、鉤爪の事で、処理を「ひっかける」ような事です。ある関数が実行される際に、独自のコードを実行させる事ができます。

フック(Hook)は、プログラム中の特定の箇所に、利用者が独自の処理を追加できるようにする仕組みである。また、フックを利用して独自の処理を追加することを「フックする」という。

あの処理がどこで呼ばれてるかわからないときに

具体例をひとつ。例えばjQueryで「どこかのタイミングで勝手にクラス名が追加されてしまって困っている」という場合を考えます。その場合は .addClass()を「フック」して、ログ出力の処理を追加します。

jQuery.fn.addClass_org = jQuery.fn.addClass;  // 元の関数を保存しておく
jQuery.fn.addClass = function() {
  // 実行時の状況をログ出力
  console.log(this, arguments);
  console.trace();  // スタックトレース(どの関数から呼ばれてきたか。IEにはありません。)

  // 何食わぬ顔で、元の処理を実行(returnを忘れがちなので注意。)
  return jQuery.fn.addClass_org.apply(this, arguments);
};

return (fn).apply(this, arguments)で同じコンテキスト、同じ引数で元の関数を実行し、さらに同じ戻り値を返します。

これで元の処理を保ちつつ、前後に独自の処理を記述する事ができるわけです。

なお実際は、頻繁に利用されるものでやると出力が膨大になってしまうので、状態で判定して絞るとか、工夫が必要になると思います。

既存のイベントハンドラーを尊重したいときに

もうひとつの例は、.onclick等のイベントハンドラーについてです。イベントリスナーと違いひとつしか指定できないので、既に指定がある場合は、それを上書きする事になってしまいます。

そんなときも処理をフックしてやると、喧嘩しないで済むかもしれません。

var onload_org = window.onload;
window.onload = function() {
  // 自分の処理
  ...

  // 元の処理
  return onload_org.apply(this, arguments);
};

もちろんイベントリスナーを使えば解決なんですが、古いコードを触るなと言われる場面もある事でしょう。

ちなみに上記のコードを繰り返す事でハンドラーに幾つでも関数を設定できますが、その場合は保存用の変数名onload_orgが被らないように注意してください。クロージャに閉じ込める事ができれば安心です。

関連