LoginSignup
56
49

More than 5 years have passed since last update.

全力で ES Decorator使ってみた

Last updated at Posted at 2015-09-14

Proposalはここ
wycats/javascript-decorators

stage: 1 なのでまだまだ仕様変わる可能性あり。

typescriptやbabelでつかいたい場合は次のようなコンパイラオプションが必要

tsc --experimentalDecorators
babel --stage=1

デコレータの振る舞い

  • クラスデコレータは Class Functionをとって Class Function を返す関数
  • クラスの関数デコレータは instance とname と descriptor を引数に descriptorを返す関数
  • 引数を取るデコレータは、関数を返す関数として実装する(部分適用っぽい感じ)
  • クラスに所属しない関数へのデコレータはbabel/typescriptともに未実装?

使ってみた感想

  • 元の振る舞いを変えずに参照透過を維持するような関数が見通しが良い
  • 用途は以下が考えられる
    • DI
    • Logging
    • Memoize
    • Mixin
    • アノテーション付与
    • テスト用ヘルパ

結局Javaやpythonと同じような用途になると思う。

変わった用途として、pythonのflaskとかはAPI ルーティングで使ってたっけ。

@get('/')
def index():
  ...

こんなやつ

Debug Decorator

作ってみた。関数の振る舞いを変えずに入出力をconsole.log で吐くデコレータ。

npm install @mizchi/debug-decorator で入る

const debug = require('@mizchi/debug-decorator');
class X {
  @debug
  get name() {
    return 'xxx';
  }

  @debug
  square(n: number){
    return n * n;
  }
}
const x = new X;
x.name;
const r = x.square(2);

こんなログが出る。

debug: get name - xxx
debug: call square
        [args] 2
        [return] 4   

それぞれにdescriptorがあるかどうか判定してオリジナルのものをラップした関数に書き換えている。

コードはこんな感じ

module.exports = function debug(target, name, descriptor) {
  if (descriptor.value) {
    const original = descriptor.value.bind(target);
    descriptor.value = (...args) => {
      const val = original(...args);
      console.log(`debug: call ${name}\n\t[args]`, ...args, `\n\t[return] ${val}`)
      return val;
    }
  }
  if (descriptor.get) {
    const originalGet = descriptor.get.bind(target);
    descriptor.get = () => {
      const val = originalGet();
      console.log(`debug: get ${name} - ${val}`);
      return val;
    }
  }
  if (descriptor.set) {
    const originalSet = descriptor.set.bind(target);
    descriptor.set = (val) => {
      const r = originalSet(val);
      console.log(`debug: set ${name} - ${val}`);
      return r;
    }
  }
  return descriptor;
}

Dependency Injection

Decorator, たしか元々TypeScript with AngularにDIを、みたいな話から始まってた話、とりあえず作ってみた

mizchi/injector

npm install @mizchi/injector で入る

サンプルコード

const createInjector = require('@mizchi/injector');
const rootInjector = createInjector();
const injector = rootInjector.createChildInjector(); // can search parents
class A {}
class B {}

injector.mapSingleton(A);
injector.mapValue(B, new B);
// injector.unmap(B); // to remove
@injector({a: A, b: B})
class T {
  a: A;
  b: B;
}
const t = new T();
assert.ok(t.a instanceof A);
assert.ok(t.b instanceof B);

T に対して injectorがgetterを定義することでメンバアクセス時にDIから参照を解決する。
自分が唯一業務で使ったことがあるDIの Robotlegs AS3 Micro-Architecture - index.html を参考にした。

これもうちょっとちゃんと設計しないと治安が悪くなりそうなので、もっと良いAPIを考える。

56
49
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
56
49