#TypeScript デコレータ

導入

デコレータは、クラスの定義時にクラスの動作を変更するために使用される構文構造です。

構文的には、デコレータには次の特徴があります。

(1) 最初の文字 (またはプレフィックス) は @ で、その後に式が続きます。

(2) @ 以降の式は関数でなければなりません (関数は実行後に取得できます)。

(3) この関数は、変更されたオブジェクトのいくつかの関連する値をパラメータとして受け取ります。

(4) この関数は値を返さないか、変更されたターゲット オブジェクトを置き換える新しいオブジェクトを返します。

たとえば、デコレータとして使用される関数 Injectable() がある場合、それを @Injectable として記述し、特定のクラスの前に配置する必要があります。

@Injectable クラス A {
  // ...
}

上記の例では、デコレータ @Injectable のおかげで、クラス A の動作が実行時に変更されます。

以下は最も単純なデコレータです。

関数 simpleDecorator() {
  console.log('こんにちは');
}

@simpleDecorator
クラス A {} // 「こんにちは」

上記の例では、関数 simpleDecorator() がデコレータとして使用され、クラス A にアタッチされており、コードが解析されるときにログの行が出力されます。

上記のコードをコンパイルすると、デコレータ パラメータが使用されていないことを示すエラーが報告されます。次に、デコレータにパラメータを追加して、コードを実行するようにします。

関数 simpleDecorator(
  値:任意、
  コンテキスト:任意
) {
  console.log(`こんにちは、${context.kind} ${context.name}`);
  戻り値;
}

@simpleDecorator
class A {} // 「こんにちは、クラス A です」

上記のコードは正常にコンパイルできます。コードの意味についてはここでは説明しません。クラス A が実行前にデコレーター simpleDecorator() を実行し、自動的にパラメーターをデコレーターに渡すことを理解している限り。

デコレータにはさまざまな形式がありますが、基本的には「@」記号の後に式を追加するだけです。以下はすべて法的な装飾者です。

@myFunc
@myFuncFactory(arg1, arg2)

@libraryModule.prop
@someObj.メソッド(123)

@(wrap(dict['prop']))

@ の後の式は最終実行後の関数である必要があることに注意してください。

サブクラスを使用して親クラスを変更する場合と比較して、デコレータはより簡潔で洗練されていますが、欠点は、あまり直感的ではなく、その機能にもいくつかの制限があることです。したがって、デコレータは通常、特定の動作をクラスに追加するためにのみ使用されます。

@frozen クラス Foo {

  @configurable(false)
  @enumerable(true)
  方法() {}

  @スロットル(500)
  高価なメソッド() {}
}

上記の例では、4 つのデコレータがあり、1 つはクラス自体 (@frozen) で使用され、他の 3 つはクラス メソッド (@configurable@enumerable@throttle) で使用されます。 。これらは、コードの可読性を高め、意図を明確に表現するだけでなく、クラスの機能を追加または変更するための便利な手段も提供します。

デコレータのバージョン

TypeScript は初期の頃からデコレータをサポートしてきました。ただし、デコレータの構文はその後変更されました。 ECMAScript 標準委員会によって最終的に採用された構文標準は、TypeScript の初期に使用されていた構文とは大きく異なります。

現在、TypeScript 5.0 は両方のデコレータ構文をサポートしています。標準構文は直接使用できますが、従来の構文では --experimentalDecorators コンパイル パラメータをオンにする必要があります。

$ tsc --target ES5 --experimentalDecorators

この章ではデコレータの標準構文について説明し、次の章では従来の構文について説明します。

デコレータ構造体

デコレータ関数の型定義は以下のとおりです。

タイプ デコレータ = (
  値: DecoratedValue、
  コンテクスト: {
    種類: 文字列;
    名前: 文字列記号;
    addInitializer?(初期化子: () => void): void;
    静的?: ブール値;
    プライベート?: ブール値;
    アクセス: {
      get?(): 不明;
      セット?(値: 不明): 無効;
    };
  }
) => 置換値;

上記のコードでは、Decorator はデコレーターの型定義です。使用時に「value」と「context」の2つのパラメータを受け取る関数です。

  • value: 装飾されるオブジェクト。
  • context: コンテキスト オブジェクト。TypeScript は、このオブジェクトを記述するためのネイティブ インターフェイス ClassMethodDecoratorContext を提供します。
関数デコレータ(
  値:任意、
  コンテキスト:ClassMethodDecoratorContext
) {
  // ...
}

上記はデコレーター関数で、2 番目のパラメーター context の型は ClassMethodDecoratorContext として記述できます。

context オブジェクトのプロパティは、装飾されるオブジェクトに応じて異なります。2 つのプロパティ (kindname) のみが必須で、その他はオプションです。

(1) kind: 装飾されるオブジェクトのタイプを示す文字列。次の値を取ることができます。

  • 'クラス'
  • '方法' -「ゲッター」
  • 「セッター」
  • '分野'
  • 「アクセサ」

つまり、デコレータは全部で 6 種類あります。

(2) name: 文字列またはシンボル値、クラス名、属性名などの装飾されたオブジェクトの名前。

(3) addInitializer(): クラスの初期化ロジックを追加するために使用される関数。以前は、このロジックは通常、メソッドを初期化するためにコンストラクターに配置されていましたが、現在は関数の形式で addInitializer() メソッドに渡されます。 addInitializer() には戻り値がないことに注意してください。

(4) private: 装飾されたオブジェクトがクラスのプライベート メンバーであるかどうかを示すブール値。

(5) static: 装飾されたオブジェクトがクラスの静的メンバーであるかどうかを示すブール値。

(6) access: 特定の値の get および set メソッドを含むオブジェクト。

クラスデコレータ

クラス デコレータの種類については以下で説明します。

タイプ ClassDecorator = (
  値: 関数、
  コンテクスト: {
    種類: 'クラス';
    名前: 文字列 | 未定義;
    addInitializer(初期化: () => void): void;
  }
) => 関数 void;

クラス デコレータは、value (現在のクラス自体) と context (コンテキスト オブジェクト) の 2 つのパラメータを受け入れます。このうち、contextオブジェクトのkind属性は文字列classに固定されています。

クラス デコレータは通常、クラスを操作するために使用され、値を返す必要はありません。次の例を参照してください。

function Greeter(値, コンテキスト) {
  if (context.kind === 'クラス') {
    value.prototype.greet = function () {
      console.log('こんにちは');
    };
  }
}

@グリーター
クラス ユーザー {}

let u = 新しいユーザー();
u.greet(); // 「こんにちは」

上記の例では、クラス デコレータ @Greeter がクラス User のプロトタイプ オブジェクトに greet() メソッドを追加し、インスタンスはこのメソッドを直接使用できます。

クラス デコレータは、現在のクラスのコンストラクタを置き換える関数を返すことができます。

関数 countInstances(値:任意、コンテキスト:任意) {
  インスタンスカウント = 0 にします。

  const ラッパー = 関数 (...args:any[]) {
    インスタンスカウント++;
    const インスタンス = 新しい値(...args);
    インスタンス.カウント = インスタンス数;
    インスタンスを返します。
  typeof MyClass と同じくらい不明です。

  ラッパー.プロトタイプ = 値.プロトタイプ; //
  リターンラッパー;
}

@countInstances
クラス MyClass {}

const inst1 = 新しい MyClass();
inst1 instanceof MyClass // true
inst1.count // 1

上記の例では、クラス デコレータ @countInstances が関数を返し、クラス MyClass のコンストラクタを置き換えます。新しい構築方法では、インスタンスのカウントを実装します。新しいインスタンスが作成されるたびに、カウンターが 1 つ増加し、現在のインスタンスの数を示すためにインスタンスに count 属性が追加されます。

上記の例では、新しいコンストラクターが MyClass のプロトタイプで定義されたメンバーを確実に継承するように、2 つのプロトタイプ オブジェクトが一貫していることを保証するために A 行が特別に追加されていることに注意してください。そうしないと、新しいコンストラクター wrapper のプロトタイプ オブジェクトは、MyClass とは異なり、instanceof 演算子を渡すことができません。

クラス デコレータは、新しいクラスを返して、元のデコレートされたクラスを置き換えることもできます。

関数 countInstances(値:任意、コンテキスト:任意) {
  インスタンスカウント = 0 にします。

  戻りクラスは値を拡張します {
    コンストラクター(...args:any[]) {
      super(...args);
      インスタンスカウント++;
      this.count = インスタンス数;
    }
  };
}

@countInstances
クラス MyClass {}

const inst1 = 新しい MyClass();
inst1 instanceof MyClass // true
inst1.count // 1

上の例では、@countInstancesMyClass のサブクラスを返します。

次の例では、クラス デコレータを使用して、クラスの新しいインスタンスを作成するための「new」コマンドの使用を禁止しています。

関数 functionCallable(
  値:任意、{種類}:任意
):どれでも {
  if (種類 === 'クラス') {
    return 関数 (...args:any) {
      if (new.target !== 未定義) {
        throw new TypeError('この関数は新規呼び出しできません');
      }
      新しい値を返します(...args);
    }
  }
}

@functionCallable
クラス人 {
  名前:文字列;
  コンストラクター(名前:文字列) {
    this.name = 名前;
  }
}

// @ts-ignore
const robin = 人('ロビン');
robin.name // 'ロビン'

上記の例では、クラス デコレータ @functionCallable は新しいコンストラクタを返します。これは、new.target が空でない場合、new コマンドを介して呼び出されることを意味し、エラーが報告されます。

クラス デコレータのコンテキスト オブジェクト contextaddInitializer() メソッドは、クラスが完全に定義された後に実行されるクラスの初期化関数を定義するために使用されます。

関数カスタム要素(名前: 文字列) {
  return <Input extends new (...args: any) => any>(
    値: 入力、
    コンテキスト: ClassDecoratorContext
  ) => {
    context.addInitializer(function() {
      CustomElements.define(名前, 値);
    });
  };
}

@customElement("ハローワールド")
class MyComponent extends HTMLElement {
  コンストラクター() {
    素晴らしい();
  }
  ConnectedCallback() {
    this.innerHTML = `<h1>Hello World</h1>`;
  }
}

上記の例では、クラス MyComponent が定義された後、クラス デコレータ @customElement() によって指定された初期化関数が自動的に実行されます。この関数は、現在のクラスを指定された名前 (この場合は <) として登録します。 hello-world> ) カスタム HTML 要素。

メソッドデコレータ

メソッド デコレータは、クラスのメソッドを装飾するために使用されます。その種類を以下に説明します。

タイプ ClassMethodDecorator = (
  値: 関数、
  コンテクスト: {
    種類: 'メソッド';
    名前: 文字列記号;
    静的: ブール値;
    プライベート: ブール値;
    アクセス: { get: () => 不明 };
    addInitializer(初期化: () => void): void;
  }
) => 関数 void;

上記の型によれば、メソッド デコレータは 2 つのパラメータ、valuecontext を受け取る関数です。

パラメータ value はメソッド自体であり、パラメータ context は次のプロパティを持つコンテキスト オブジェクトです。

  • kind: 値は文字列 method に固定され、現在メソッド デコレータであることを示します。
  • name: 修飾されたメソッド名。タイプは文字列またはシンボル値です。
  • static: 静的メソッドかどうかを示すブール値。このプロパティは読み取り専用です。
  • private: プライベート メソッドかどうかを示すブール値。このプロパティは読み取り専用です。
  • access: オブジェクト。メソッドのアクセサーが含まれますが、値の取得には get() メソッドのみが使用され、値を割り当てるための set() メソッドはありません。
  • addInitializer(): メソッドに初期化関数を追加します。

メソッド デコレータはクラスの元のメソッドを書き換えます。これは基本的に次の操作と同等です。

関数トレース(decoratedMethod) {
  // ...
}

クラスC {
  @トレース
  toString() {
    「C」を返します。
  }
}

// `@trace` は以下と同等です
// C.prototype.toString = トレース(C.prototype.toString);

上記の例では、@trace はメソッド toString() のデコレータであり、その効果は最後の行の toString() を書き換えることと同等です。

メソッド デコレーターが新しい関数を返す場合、装飾された元の関数が置き換えられます。

関数 replaceMethod() {
  戻り関数 () {
    return `調子はどうですか、${this.name}?`;
  }
}

クラス人 {
  コンストラクター(名前) {
    this.name = 名前;
  }

  @replaceMethod
  こんにちは() {
    return `こんにちは ${this.name}!`;
  }
}

const robin = 新しい人('ロビン');

robin.hello() // 「調子はどうですか、ロビン?」

上記の例では、デコレータ @replaceMethod によって返される関数が新しい hello() メソッドになります。

別の例を示します。

クラス人 {
  名前: 文字列;
  コンストラクター(名前: 文字列) {
    this.name = 名前;
  }

  @ログ
  挨拶する() {
    console.log(`こんにちは、私の名前は${this.name}です。`);
  }
}

function log(originalMethod:any, context:ClassMethodDecoratorContext) {
  const メソッド名 = String(context.name);

  function replaceMethod(this: any, ...args: any[]) {
    console.log(`LOG: メソッド '${methodName}' に入ります。`)
    const result =originalMethod.call(this, ...args);
    console.log(`LOG: メソッド '${methodName}' を終了します。`)
    結果を返します。
  }

  replaceMethod を返します。
}

const person = 新しい人('張三');
person.greet()
// "ログ: メソッド「greet」を入力します。"
// 「こんにちは、私の名前は張三です。」
// "ログ: メソッド 'greet' を終了します。"

上記の例では、デコレータ @log の戻り値は関数 replacementMethod であり、元のメソッド greet() を置き換えます。 replacementMethod() 内で、originalMethod.call() を実行することで元のメソッドの呼び出しが完了します。

メソッド デコレータを使用すると、クラス メソッドを遅延実行に変えることができます。

関数遅延(ミリ秒:数値 = 0) {
  戻り関数 (値、コンテキスト) {
    if (context.kind === "メソッド") {
      return 関数 (...引数: 任意[]) {
        setTimeout(() => {
          value.apply(this, args);
        }、ミリ秒);
      };
    }
  };
}

クラスロガー{
  @遅延(1000)
  log(msg: 文字列) {
    console.log(`${msg}`);
  }
}

let logger = new Logger();
logger.log("Hello World");

上記の例では、メソッド デコレータ @lay(1000) はメソッド log() の実行を 1 秒 (1000 ミリ秒) 遅らせます。ここでの実際のメソッド デコレータは、delay() の実行後に返される関数です。 delay() の関数は、実行を遅らせる時間を設定するためのパラメータを受け取ることです。この高階関数を通じてデコレータを返す方法は「ファクトリ パターン」と呼ばれ、ファクトリのようにデコレータのモデルを生成できます。

メソッド デコレータの context パラメータには addInitializer() メソッドがあります。これは、クラスの初期化フェーズ中にコールバック関数を追加するために使用されるフック メソッドです。このコールバック関数は、addInitializer() のパラメータとして渡され、属性 (フィールド) の初期化よりも前に、コンストラクター メソッドの実行中に実行されます。

以下は addInitializer() メソッドの例です。多くの場合、クラス メソッドはコンストラクターの this にバインドする必要があることがわかっています。

クラス人 {
  名前: 文字列;
  コンストラクター(名前: 文字列) {
    this.name = 名前;

    //greet() がこれをバインドします
    this.greet = this.greet.bind(this);
  }

  挨拶する() {
    console.log(`こんにちは、私の名前は${this.name}です。`);
  }
}

const g = 新しい人('張三').greet;
g() // 「こんにちは、私の名前は張三です。」

上の例では、クラス person のコンストラクター内で、thisgreet() メソッドにバインドされています。この行がないと、greet() を変数 g に代入して呼び出した場合、エラーが報告されます。

「this」のバインディングは、クラスの初期化フェーズ中に行う必要があるため、コンストラクター内に配置する必要があります。これで、メソッド デコレータの addInitializer() 内に移動できるようになりました。

関数バインド(
  オリジナルメソッド:任意、コンテキスト:ClassMethodDecoratorContext
) {
  const メソッド名 = context.name;
  if (context.private) {
    throw new Error(`プライベート メソッド ${methodName を文字列としてバインドできません`);
  }
  context.addInitializer(function() {
    this[メソッド名] = this[メソッド名].bind(this);
  });
}

上の例では、バインディング thisaddInitializer() メソッドに転送されます。

addInitializer() を使用して、選択したメソッド名をコレクションに入れる別の例を見てみましょう。

関数コレクト(
  価値、
  {名前、addInitializer}
) {
  addInitializer(関数() {
    if (!this.collectedMethodKeys) {
      this.collectedMethodKeys = new Set();
    }
    this.collectedMethodKeys.add(名前);
  });
}

クラスC {
  @集める
  toString() {}

  @集める
  [Symbol.iterator]() {}
}

const inst = 新しい C();
inst.collectedMethodKeys // new Set(['toString', Symbol.iterator])

上記の例では、メソッド デコレータ @collect は、装飾されたメンバー名を Set コレクション collectedMethodKeys に追加します。

プロパティデコレーター

プロパティ デコレータは、クラスの先頭で定義されたプロパティ (フィールド) を装飾するために使用されます。その種類を以下に説明します。

type ClassFieldDecorator = (
  値: 未定義、
  コンテクスト: {
    種類: 'フィールド';
    名前: 文字列記号 |
    静的: ブール値;
    プライベート: ブール値;
    アクセス: { get: () => 不明、set: (値: 不明) => void };
    addInitializer(初期化: () => void): void;
  }
) => (初期値: 不明) => 不明 |

デコレータの最初のパラメータ value の型は undefined であることに注意してください。これは、このパラメータが実際には役に立たず、デコレータは value からデコレートされたプロパティの値を取得できないことを意味します。さらに、第 2 パラメータの context オブジェクトの kind 属性の値は、"プロパティ" や "属性" ではなく、文字列 field であることに注意する必要があります。

プロパティ デコレーターは、値を返さないか、装飾されたプロパティを初期化するために自動的に実行される関数を返します。この関数のパラメータは装飾されたプロパティの初期値であり、この関数の戻り値はプロパティの最終値です。

関数のログ(値、コンテキスト) {
  const {種類、名前} = コンテキスト;
  if (種類 === 'フィールド') {
    戻り関数 (初期値) {
      console.log(`${name} を値 ${initialValue} で初期化中`);
      初期値を返します。
    };
  }
}

クラス カラー {
  @ログ名 = 'green';
}

const color = new Color();
// "名前を値緑色で初期化しています"

上記の例では、プロパティ デコレータ @logged がプロパティ name を装飾しています。 @logged の戻り値は、属性 name を初期化するために使用される関数です。そのパラメータ initialValue は、属性 name の初期値 green です。この関数は、新しいインスタンス オブジェクト color を作成するときに自動的に実行されます。

プロパティ デコレータの戻り値関数を使用して、プロパティの初期値を変更できます。

関数twice() {
  初期値を返す => 初期値 * 2;
}

クラスC {
  @2回
  フィールド = 3;
}

const inst = 新しい C();
インスタフィールド // 6

上記の例では、プロパティ デコレータ @twice は関数を返します。関数の戻り値は、プロパティ field の初期値に 2 を乗算した値であるため、プロパティ field の最終値は 6 になります。

プロパティ デコレーターのコンテキスト オブジェクト contextaccess プロパティは、装飾されたプロパティへのアクセサーを提供します。以下の例を参照してください。

受け入れましょう。

関数exposeAccess(
  値、{アクセス}
) {
  acc = アクセス;
}

クラス カラー {
  @exposeAccess
  名前 = 'グリーン'
}

const green = 新しいColor();
green.name // '緑'

acc.get(green) // '緑'

acc.set(緑, '赤');
green.name // '赤'

上記の例では、access には属性 name のアクセサーが含まれており、これを使用してこの属性に値を取得および割り当てることができます。

ゲッター デコレータ、セッター デコレータ

ゲッター デコレーターとセッター デコレーターは、それぞれクラスのゲッターとセッターのデコレーターです。それらの種類を以下に説明します。

type ClassGetterDecorator = (
  値: 関数、
  コンテクスト: {
    種類: 'ゲッター';
    名前: 文字列記号;
    静的: ブール値;
    プライベート: ブール値;
    アクセス: { get: () => 不明 };
    addInitializer(初期化: () => void): void;
  }
) => 関数 void;

type ClassSetterDecorator = (
  値: 関数、
  コンテクスト: {
    種類: 'セッター';
    名前: 文字列記号;
    静的: ブール値;
    プライベート: ブール値;
    アクセス: { セット: (値: 不明) => void };
    addInitializer(初期化: () => void): void;
  }
) => 関数 void;

getter デコレータのコンテキスト オブジェクト contextaccess 属性には get() メソッドのみが含まれ、setter デコレータの access プロパティには set() メソッドのみが含まれることに注意してください。

これら 2 つのデコレータは値を返さないか、元のゲッターまたはストアラーを置き換える関数を返します。

次の例では、以降の読み取りを高速化するために、valuer の結果を属性として保存します。

クラスC {
  @怠け者
  値を取得() {
    console.log('計算中...');
    「高価な計算結果」を返します。
  }
}

関数遅延(
  値:任意、
  {種類、名前}:任意
) {
  if (kind === 'getter') {
    戻り関数 (this:any) {
      const result = value.call(this);
      Object.defineProperty(
        これ、名前、
        {
          値: 結果、
          書き込み可能: false、
        }
      );
      結果を返します。
    };
  }
  戻る;
}

const inst = 新しい C();
初期値
//計算中...
// '高価な計算結果'
初期値
// '高価な計算結果'

上記の例では、初めて inst.value が読み取られたときに計算が実行され、この属性が読み取られた場合、デコレータ @lazy は結果を読み取り専用属性 value に格納します。それ以降、計算は実行されません。

アクセサ デコレータ

デコレータ構文では、新しい属性修飾子「accessor」が導入されています。

クラスC {
  アクセサ x = 1;
}

上記の例では、accessor 修飾子は、プライベート プロパティ x に作用するパブリック プロパティ x のゲッターおよびレジスタを自動的に生成することに相当します。 (public x は private x と同じプロパティではないことに注意してください。) つまり、上記のコードは次のコードと同等です。

クラスC {
  #x = 1;

  get x() {
    これを返します。#x;
  }

  set x(val) {
    this.#x = val;
  }
}

accessor は、静的プロパティおよびプライベート プロパティとともに使用することもできます。

クラスC {
  静的アクセサー x = 1;
  アクセサ #y = 2;
}

アクセサデコレータの種類は以下のとおりです。

type ClassAutoAccessorDecorator = (
  価値: {
    取得: () => 不明;
    セット: (値: 不明) => void;
  }、
  コンテクスト: {
    種類: "アクセサ";
    名前: 文字列記号;
    アクセス: { get(): 不明、set(値: 不明): void };
    静的: ブール値;
    プライベート: ブール値;
    addInitializer(初期化: () => void): void;
  }
) => {
  取得?: () => 不明;
  set?: (値: 不明) => void;
  init?: (初期値: 不明) => 不明;
無効;

アクセサ デコレータの value パラメータは、get() メソッドと set() メソッドを含むオブジェクトです。デコレータは値を返さないか、元の get() メソッドと set() メソッドを置き換える新しいオブジェクトを返すことができます。さらに、デコレーターによって返されるオブジェクトには、プライベート プロパティの初期値を変更するための init() メソッドを含めることもできます。

以下に例を示します。

クラスC {
  @logg アクセサー x = 1;
}

関数 logged(値, { 種類, 名前 }) {
  if (種類 === "アクセサ") {
    let { get, set } = 値;

    戻る {
      得る() {
        console.log(`${name} の取得`);

        get.call(this) を返します。
      }、

      set(val) {
        console.log(`${name} を ${val} に設定します`);

        return set.call(this, val);
      }、

      init(初期値) {
        console.log(`${name} を値 ${initialValue} で初期化中`);
        初期値を返します。
      }
    };
  }
}

c = 新しい C(); とします。

c.x;
// x を取得する

c.x = 123;
// x を 123 に設定します

上記の例では、デコレーター @logged が属性 x のセイバーおよびゲッターにログ出力を追加します。

デコレータの実行順序

デコレータの実行は 2 つのフェーズに分かれています。

(1) 評価: @ 記号の後の式の値を計算し、その結果は関数になります。

(2) 適用:デコレータを評価して得られた関数を装飾オブジェクトに適用します。

つまり、デコレータが実行される順序は、すべてのデコレータ式が現在のクラスに適用される前に評価されるような順序です。

デコレータを適用する場合、順序はメソッド デコレータ、プロパティ デコレータ、クラス デコレータの順です。

以下の例を参照してください。

関数 d(str:string) {
  console.log(`evaluate @d(): ${str}`);
  戻る (
    値: 任意、コンテキスト: 任意
  ) => console.log(`アプリケーション @d(): ${str}`);
}

関数ログ(文字列:文字列) {
  コンソール.ログ(文字列);
  文字列を返します。
}

@d('クラスデコレータ')
クラス T {
  @d('静的プロパティデコレータ')
  static staticField = log('静的属性値');

  @d('プロトタイプメソッド')
  [log('計算方法名')]() {}

  @d('インスタンス属性')
  インスタンスフィールド = log('インスタンス属性値');

  @d('静的メソッドデコレータ')
  静的 fn(){}
}

上記の例では、クラス T には、クラス デコレータ、静的プロパティ デコレータ、メソッド デコレータ、プロパティ デコレータの 4 つのデコレータがあります。

運用結果は以下の通りです。

@d() の評価: クラス デコレータ
@d() の評価: 静的プロパティ デコレータ
@d() を評価する: プロトタイプ メソッド
計算方法名
@d() の評価: インスタンスのプロパティ
@d() の評価: 静的メソッド デコレータ
@d() を適用: 静的メソッド デコレーター
@d(): プロトタイプメソッドを適用します
@d() を適用: 静的プロパティ デコレータ
@d(): インスタンスのプロパティを適用します
@d(): クラスデコレータを適用する
静的プロパティ値

ご覧のとおり、クラスがロードされると、コードは次の順序で実行されます。

(1) デコレータの評価: このステップでは、デコレータの値が計算されます。最初にクラス デコレータ、次にクラス内のデコレータが出現順に計算されます。

プロパティ名またはメソッド名が計算値 (この場合は「計算されたメソッド名」) の場合、対応するデコレーターが評価された後にそれら自体も評価されることに注意してください。

(2) デコレータ アプリケーション: 実際にデコレータ関数を実行し、対応するメソッドやプロパティと組み合わせます。

最初に静的メソッド デコレータが適用され、次にプロトタイプ メソッド デコレータと静的プロパティ デコレータ、次のインスタンス プロパティ デコレータ、最後にクラス デコレータが適用されます。

「インスタンス属性値」はクラスの初期化フェーズでは実行されず、クラスがインスタンス化されるまで実行されないことに注意してください。

メソッドまたはプロパティに複数のデコレータがある場合、内側のデコレータが最初に実行され、次に外側のデコレータが実行されます。

クラス人 {
  名前: 文字列;
  コンストラクター(名前: 文字列) {
    this.name = 名前;
  }

  @bound
  @ログ
  挨拶する() {
    console.log(`こんにちは、私の名前は${this.name}です。`);
  }
}

上記の例では、greet() には 2 つのデコレータがあり、内側の @log が最初に実行され、外側の @bound が取得された結果に基づいて実行されます。

参考リンク


作者: wangdoc

アドレス: https://wangdoc.com/

ライセンス: クリエイティブ・コモンズ 3.0