JavaScriptでAOP

最近,JavaScriptの関数オブジェクトの便利さにほれぼれしている.たとえば,こんな風に

element.onclick = function() {
  // hogehoge
}

イベントハンドラを後付けできたりして便利な事この上ない.


そこで,ふと思った.すでに設定されているイベントハンドラ上書きしないで処理を追加する事はできないものかと.試してみたら割と簡単で,こんな感じで書けた.

var oldfunc = element.onclick;
element.onclick = function() {
  // hogehoge
  var result = oldfunc();
  // hagehage
  return result;
}

ただこれ欠点があって,元の関数が引数もらってても渡す術がない.たとえば,こんなヤツが困る.

<input type="text" onblur="setComma(this)">

そこで,マジメにアスペクトかけないとダメだなぁと思って調べてみたら,やっぱりあるのよね>JavaScriptAOP

ここで紹介してあったakmさんの実装をパクらせてもらいました.とりあえず必要なのはaroundアドバイスだけだし,ジョインポイントも1個で良いんで,それっぽい劣化コピーを作成.試しに,onblur, onfocusでカンマ編集を行っているテキストフィールドに対してウィーブしてみたよ.

<html>
<script type="text/javascript">
  Object.Aspect = {
    _around: function(target, methodName, aspect) {
      var method = target[methodName];
      target[methodName] = function() {
        var invocation = {
          "target" : this,
          "method" : method,
          "methodName" : methodName,
          "arguments" : arguments,
          "proceed" : function() {
            return method.apply(target, this.arguments);
          }
        };
        return aspect.apply(null, [invocation]);
      };
    },
    around: function(target, methodName, aspect) {
      this._around(target, methodName, aspect);
    }
  }
</script>
<script type="text/javascript">
  function setComma(element){
    var destStr = element.value;
    var tmpStr = "";
    while (destStr != (tmpStr = destStr.replace(/^([+-]??d+)(?d?d?d)/,"$1,$2"))) {
      destStr = tmpStr;
    }
    element.value= destStr;
  }
  function supComma(element){
    element.value = element.value.split(",").join("");
  }
</script>

<body>

  <input type="text" id="t1" value="1000" onfocus="supComma(this)" onblur="setComma(this)">
  <input type="text" id="t2">

<script type="text/javascript">
  var aspect = function(invocation) {
    alert("before");
    var result = invocation.proceed();
    alert("after");
    return result;
  }
  var t1 = document.getElementById("t1");
  Object.Aspect.around(t1, "onblur", aspect);
</script>
</body>
</html>

すごいなーこれ,実装考えたid:akmさんもスゴイし,そもそもJavaScriptって侮れない言語だよなー.ウィーブするコードなんか,これだけじゃん.

<script type="text/javascript">
  var aspect = function(invocation) {
    alert("before");
    var result = invocation.proceed();
    alert("after");
    return result;
  }
  var t1 = document.getElementById("t1");
  Object.Aspect.around(t1, "onblur", aspect);
</script>

うほっ,これで元のコードを汚染せずに後付けで機能追加できそうだ.


ちなみに,どんなことを考えていたかと言うと,こんなツールチップみたいなのを入力項目に後付けしてみたかったのダ.同じ流れで,ボタンに対して二重送信防止を後付けすることも出来そうダナ.

うひひひ,久しぶりに身震いした.
#なんか落とし穴があるんじゃないか?