クロージャを使ったデコレータをJavaScriptでも自前実装

昨日の続き
Pythonでデコレータの引数を元に関数を動的に呼び出すアレについて。

デコレータの構造を追っていったら、中身はクロージャだったということだった。
デコレータの文法を使わないでデコレータと同じことができる。

文法を使わずやってみると本質が理解できる。

ちなみにより詳しく説明しているブログを発見したので、最後の参考の章を見ていただきたい。

Pythonでデコレータ文法を使わずデコレータを実装


def decotest(func):
    def decinner(*args , **kwargs):
        print("関数実行前")
        result = func(*args , **kwargs)
        print("関数実行後")
        return result
    return decinner

@decotest
def hoge(a):
    print("ほげだよー")
    print(a)

def foo(a):
    print("fooだよ")
    print(a)
foo = decotest(foo)

if __name__ == '__main__':
    hoge("ほげですか")
    foo("fooですか")

hogeは普通のデコレータ
fooはデコレータの文法を使わないパターン。

関数を渡して、その関数を呼び出す関数に書き換える。
詳しく書くと、関数を受け取ったら、その関数を実行する関数を戻り値として戻すので、それで書き換える。

クロージャと言う仕組みは巧妙で、戻ってくる関数の外側で宣言された変数は戻ったあとでずっと有効なので、状態を持つ関数が作れてしまう。

JavaScriptで実装してみる

この仕組みをJavaScriptに移植してみよう。

(function(){
  var print = console.log;

  var deco = function(func){
    return function() {
      print("デコレータだよ");
      result = func.apply(null,arguments);
      print("デコレータ終わり")
      return result;
    }
  }

  var foo = function(a,b){
      print("fooだよ");
      print(a);
      print(b);
    };
  foo = deco(foo);

  foo("ぬー","ぼー");

})();

やっていることは変わらない。
foo関数を渡して、fooを実行する関数でラッピングして書き換えるだけ。
これで関数の前後に共通処理を入れ込むことができる。

JavaScriptで苦労したのが、引数の渡しかた。
Pythonだと(*args , **kwargs)という、どんな引数でもドンと来いという構文があるのでいいのだけれど、
JavaScriptで同じ機能にする方法に少し悩んだ。

argumentsという変数は引数をまとめているオブジェクトがあるが、
それをそのまま関数に渡すと、第一引数にそのオブジェクトが入っていることになる。
第一引数や第二引数にそれぞれほしい。
つまりfoo関数に関して、デコレーションされようがされまいがそんなことお構いなしにいつもどおりに実装したい。

それを実現するのはapplyだ。配列やオブジェクトを渡すと、その順番で第一引数や第二引数に入ってくれる。
超便利

ともかく、複数の関数に共通の前後処理をいれこむデコレータをいろいろな言語で使えることになった。

参考

Python: 色々なデコレータの作り方と使い方、そして本質 | CUBE SUGAR STORAGE

スポンサードリンク

関連コンテンツ