デコレータ

(Web関連用語)
https://qiita.com/kerupani129/items/2b3f2cba195c0705b2e5

クラスやメソッドに機能を追加するもの。(Java でいうアノテーション)

クラス

// 定義
const F = target => {
  // target: クラス
  // return target; // なくても良い
};

// 適用
@F // ★
class Foo { }

// やっていること
class Foo { }
Foo = F(Foo) || Foo; // ★

メソッド

// 定義
const F = (target, name, descriptor) => {
  // target: クラスの prototype (※ this の代わりに使うとハマることがある)
  // name: メソッド名
  // descriptor: メソッドのディスクリプタ
  // descriptor.value: メソッドそのもの
  // descriptor.writable: false なら const
  // return descriptor; // なくても良い
};

// 適用
class Foo {
  @F // ★
  bar() {}
}

// やっていること
class Foo {
  bar() {}
}
// ★
(() => {
  let bar = Object.getOwnPropertyDescriptor(Foo.prototype, 'bar');
  bar = F(Foo.prototype, 'bar', bar) || bar;
  if (bar) Object.defineProperty(Foo.prototype, 'bar', bar);
})();

※Object.getOwnPropertyDescriptor()、Object.defineProperty() はディスクリプタを読み書きする関数。
※ディスクリプタの enumerable, configurable は普段使わないと思うので省略。

アクセサ

ほぼメソッドと同じ(ディスクリプタの内容が異なる)。

// 定義
const F = (target, name, descriptor) => {
  // target: this
  // name: メソッド名
  // descriptor: メソッドのディスクリプタ
  // descriptor.get: getter そのもの
  // descriptor.set: setter そのもの
  // return descriptor; // なくても良い
};

// 適用
class Foo {
  @F // ★
  get bar() {}
  set bar(value) {}
}

// やっていること
class Foo {
  get bar() {}
  set bar(value) {}
}
// ★ (メソッドと同じ)
(() => {
  let bar = Object.getOwnPropertyDescriptor(Foo.prototype, 'bar');
  bar = F(Foo.prototype, 'bar', bar) || bar;
  if (bar) Object.defineProperty(Foo.prototype, 'bar', bar);
})();

オブジェクトのメソッド・アクセサ

オブジェクト定義の中のメソッド・アクセサでも同様に使用することができる。

const Foo = {
  @F // ★
  bar() {},

  @F // ★
  get baz() {},
  set baz(value) {}
};

デコレータに引数を持たせる

デコレータを返す関数を定義する。

// 定義
const F = param => (target, name, descriptor) => {
  // ...
};

// 適用
class Foo {
  @F('Param') // ★ F('Param') == (target, name, descriptor) => {/* ... */}
  bar() {}
}

複数のデコレータを同時に適用する

適用したいクラス・メソッド・アクセサの宣言に近いものから順に適用される。

// 適用
class Foo {
  // ★ F → G → H
  @H
  @G
  @F
  bar() {}
}

具体的な使用例(@readonly)

// 定義
const readonly = (target, name, descriptor) => {
  descriptor.writable = false;
};

// 適用
class Foo {
  bar() {}
  @readonly // ★
  baz() {}
}

// 実行
const foo = new Foo();
foo.bar = () => {};
foo.baz = () => {}; // ★エラーが発生する
inserted by FC2 system