TypeScript-Dekoratoren: ein Werkzeug für die Metaprogrammierung

Einführung

In TypeScript sind Dekoratoren ein Werkzeug für die Metaprogrammierung. Der TypeScript-Dekorator ist eine seiner leistungsstärksten Metaprogrammierungsfunktionen. Er kann Metadaten und Verhalten zu Klassen, Methoden, Eigenschaften und Parametern hinzufügen. Er kann die Codestruktur ändern, ohne die Codelogik zu ändern. Kommentieren, erweitern, ändern und umgestalten, um den Code besser zu machen elegant, prägnant, wartbar und leicht erweiterbar. Es handelt sich um eine relativ neue Sprachfunktion, die jedoch in verschiedenen Frameworks und Bibliotheken wie Angular, NestJS, Express usw. weit verbreitet ist.

In diesem Artikel untersuchen wir die Funktionen und Verwendungsszenarien von TypeScript-Dekoratoren.

Einführung in Dekorateure

Ein Dekorator ist eine besondere Art , die zum Dekorieren von , und 函数verwendet werden kann , indem diesen Objekten Dekoratoren hinzugefügt werden . Seine Syntax ähnelt einem Kommentar, aber was er wirklich tut, ist .属性方法参数改变它们的行为在代码编译阶段进行元编程操作

Grundlegende Syntax für Dekorateure

Folgendermaßen:

@decorator
class MyClass {
    
    
    // class implementation
}

@decorator
function MyFunction() {
    
    
    // function implementation
}

@decorator
property MyProperty {
    
    
    // property implementation
}

@decorator
method MyMethod() {
    
    
    // method implementation
}

@decorator
parameter MyParameter {
    
    
    // parameter implementation
}
  • In TypeScript ist ein Dekorator eine Funktion oder ein Ausdruck, der je nach Kontext, in dem der Dekorator angewendet wird, ein oder mehrere Argumente annimmt.

Fügen Sie Metadaten zu Klassen hinzu

  • Hier ist ein Beispiel für einen einfachen Dekorator, der einer Klasse Metadaten hinzufügt:

    function MyClassDecorator(target: Function) {
          
          
        Reflect.defineMetadata("custom:my-metadata", "Hello, world!", target);
    }
    
    @MyClassDecorator
    class MyClass {
          
          
        // class implementation
    }
    
  • In diesem Dekorator verwenden wir die Reflection-API von TypeScript, Reflectum Metadaten hinzuzufügen. Der Schlüssel dieser Metadaten ist „custom:my-metadata“ und der Wert ist „Hello, world!“. Der Dekorator wird MyClassauf . MyClassDecoratorWenn er aufgerufen wird, targetist MyClassdiese Funktion. Wir können diese Metadaten für nachfolgende Vorgänge wie Serialisierung und Deserialisierung verwenden. So erhalten Sie diese Metadaten:

    const metadata = Reflect.getMetadata("custom:my-metadata", MyClass); console.log(metadata); // Output: "Hello, world!"
    
  • In diesem Beispiel Reflecthaben wir getMetadatadie Methode verwendet, um die Metadaten abzurufen. Der erste Parameter dieser Methode ist der Schlüssel der Metadaten und der zweite Parameter ist die Funktion, die diese Metadaten definiert.

Verhalten hinzufügen

  • Zusätzlich zum Hinzufügen von Metadaten können Dekorateure auch Verhalten hinzufügen. Hier ist ein Beispiel für das Hinzufügen eines Verhaltens, das Setter und Getter zu Eigenschaften einer Klasse hinzufügt:
    function MyPropertyDecorator(target: any, propertyKey: string) {
          
          
        // property getter
        const getter = function () {
          
          
            console.log(`Getting value for ${
            
            propertyKey}`);
            return this[`_${
            
            propertyKey}`];
        };
        // property setter
        const setter = function (newVal) {
          
          
            console.log(`Setting value for ${
            
            propertyKey}`);
            this[`_${
            
            propertyKey}`] = newVal;
        };
        // delete and redefine the property
        if (delete this[propertyKey]) {
          
          
            Object.defineProperty(target, propertyKey, {
          
          
                get: getter,
                set: setter,
                enumerable: true,
                configurable: true
            });
        }
    }
    
    class MyClass {
          
          
        @MyPropertyDecorator
        myProp: string;
    }
    
  • In diesem Beispiel definieren wir einen Dekorator MyPropertyDecoratornamens der myPropauf die Eigenschaft angewendet wird. Wenn der Dekorator angewendet wird, definiert er den Getter und Setter der Eigenschaft neu. In diesem Beispiel können wir myPropEigenschafts- um das Get- und Set-Verhalten zu dokumentieren.

Verwendungsszenarien von TypeScript-Dekoratoren

  • 日志记录
    • Dekorateure können Protokolle aufzeichnen, sodass Entwickler den Ausführungsstatus des Programms verstehen und das Debuggen und Optimieren des Programms erleichtern können.
      function log(target: Function) {
              
              
        console.log(`Logger: ${
                
                target.name} constructed.`)
      }
      
      @log
      class MyClass {
              
              
        constructor() {
              
              }
      }
      
      // output: Logger: MyClass constructed.
      
    • In diesem Beispiel wird das Dekorator-Funktionsprotokoll auf den Konstruktor der MyClass-Klasse angewendet. Wenn der Code kompiliert wird, ändert der Dekorator das Verhalten von MyClass, sodass das Protokoll gedruckt wird, wenn der Konstruktor aufgerufen wird.
  • 校验
    • Dekorateure können Klassen und Eigenschaften validieren, um sicherzustellen, dass sie bestimmten Spezifikationen entsprechen.
      // 类装饰器
      function validateClass(target: any) {
              
              
        // 检查类是否有name属性
        if (!target.name) {
              
              
          throw new Error("Class name is required");
        }
      }
      
      // 属性装饰器
      function validateProperty(target: any, propertyKey: string) {
              
              
        // 获取属性值
        const value = target[propertyKey];
        // 检查属性值是否为数字
        if (typeof value !== "number") {
              
              
          throw new Error(`${
                
                propertyKey} must be a number`);
        }
      }
      
      @validateClass
      class Person {
              
              
        name: string;
      
        @validateProperty
        age: number;
      
        constructor(name: string, age: number) {
              
              
          this.name = name;
          this.age = age;
        }
      }
      
      // 创建一个Person实例
      const person = new Person("Bob", 25);
      
      // 输出实例的属性
      console.log(person.name); // "Bob"
      console.log(person.age); // 25
      
      // 创建一个不合法的Person实例
      const invalidPerson = new Person("", "25");
      
      // 抛出类装饰器抛出的异常
      // Error: Class name is required
      
      // 创建一个属性不合法的Person实例
      const invalidAgePerson = new Person("Bob", "25");
      
      // 抛出属性装饰器抛出的异常
      // Error: age must be a number
      
    • Dieses Beispiel definiert zwei Dekoratorfunktionen validateClassund validateProperty. validateClassWird verwendet, um zu überprüfen, ob eine Klasse über nameeine Eigenschaft , und um eine Ausnahme auszulösen, wenn dies nicht der Fall ist. validatePropertyWird verwendet, um zu prüfen, ob der Attributwert einer Klasse eine Zahl ist, und um eine Ausnahme auszulösen, wenn dies nicht der Fall ist.
    • Dekorieren Sie im PersonUnterricht @validateClassdie Klasse mit dem Dekorateur @validatePropertyund dekorieren Sie agedas Grundstück mit dem Dekorateur. Wenn beim Erstellen Personeiner Instanz die Attribute der Klasse nicht den Anforderungen entsprechen, wird die entsprechende Ausnahme ausgelöst.
  • 性能优化
    • Dekorateure können die Leistung des Codes optimieren, indem sie beispielsweise einige Berechnungsergebnisse zwischenspeichern, um wiederholte Berechnungen zu vermeiden.
      function memoize(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
              
              
        const originalMethod = descriptor.value;
        const cache = new Map();
      
        descriptor.value = function(...args) {
              
              
          const key = JSON.stringify(args);
          if (cache.has(key)) {
              
              
            return cache.get(key);
          }
          const result = originalMethod.apply(this, args);
          cache.set(key, result);
          return result;
        };
      
        return descriptor;
      }
      
      class Calculator {
              
              
        @memoize
        factorial(n: number): number {
              
              
          console.log(`Calculating factorial of ${
                
                n}`);
          if (n === 0) {
              
              
            return 1;
          }
          return n * this.factorial(n - 1);
        }
      }
      
      const calc = new Calculator();
      
      console.log(calc.factorial(5)); // Calculating factorial of 5, 120
      console.log(calc.factorial(5)); // 120
      
      console.log(calc.factorial(10)); // Calculating factorial of 10, 3628800
      console.log(calc.factorial(10)); // 3628800
      
    • In diesem Beispiel wird eine Dekoratorfunktion definiert, memoizeum das Berechnungsergebnis einer bestimmten Methode der Klasse zwischenzuspeichern. Rufen Sie in memoizeder Funktion zuerst die ursprüngliche Methode ab und erstellen Sie dann einen Cache Map. Überschreibt die ursprüngliche Methode und konvertiert beim Aufruf das übergebene Argument in eine Zeichenfolge als Cache-Schlüssel. Wenn im Cache bereits ein diesem Schlüssel entsprechender Wert vorhanden ist, wird das Ergebnis im Cache direkt zurückgegeben, andernfalls wird das Ergebnis der Methode berechnet und im Cache gespeichert.
    • Verwenden Sie in Calculatorder Klasse @memoizeden Dekorator, um factorialdie Methode , dh diese Methode in eine Methode mit Caching-Funktion umzuwandeln. Rufen Sie die Methode nach dem Erstellen Calculatorder Instanz factorialzweimal auf: Beim ersten Mal müssen Sie die Fakultät berechnen, und beim zweiten Mal erhalten Sie den Fakultätswert direkt aus dem Cache, um wiederholte Berechnungen zu vermeiden.
  • 错误处理
    • Dekorateure können Fehler im Programm behandeln, z. B. Ausnahmen abfangen und Fehlermeldungen ausgeben, was für Entwickler praktisch ist, um Fehler zu überprüfen und zu beseitigen.
      function catchError(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
              
              
        const originalMethod = descriptor.value; // 保存原始方法
        descriptor.value = function (...args: any[]) {
              
              
          try {
              
              
            return originalMethod.apply(this, args); // 调用原始方法并返回结果
          } catch (error) {
              
              
            console.error(`Error occurred in ${
                
                propertyKey}:`, error); // 输出错误信息
          }
        };
      }
      
      class Example {
              
              
        @catchError
        public divide(a: number, b: number): number {
              
              
          return a / b; // 可能会抛出异常
        }
      }
      
      const example = new Example();
      example.divide(10, 0); // 调用方法,抛出异常并输出错误信息
      
    • In diesem Beispiel definieren wir einen catchErrorDekorator und wenden ihn auf die MethodeExample der Klasse an. divideWenn wir example.divide(10, 0)die Methode , wird eine Ausnahme ausgelöst, da der Divisor 0 ist. Dieser Dekorator fängt die Ausnahme ab und gibt eine Fehlermeldung aus.
  • Zusätzlich zu den oben genannten Beispielen können Dekoratoren auch in vielen anderen Situationen verwendet werden, z. B. 控制访问权限, 验证输入参数usw. 缓存数据, wodurch Entwickler ein flexibleres und leistungsfähigeres Metaprogrammierungstool erhalten.

Zusammenfassen

Dekoratoren sind ein sehr leistungsfähiges Metaprogrammierungstool, das die Wartbarkeit und Erweiterbarkeit von TypeScript erheblich verbessern kann. Dekorateure können Metadaten und Verhalten zu Klassen, Methoden, Eigenschaften und Parametern hinzufügen. Mithilfe von Dekoratoren können Entwickler Code bequemer verwalten und ihn einfacher erweitern und ändern.

おすすめ

転載: blog.csdn.net/McapricornZ/article/details/131295384