Heim >Web-Frontend >js-Tutorial >Dekoratoren in TypeScript verstehen: Ein Ansatz nach den ersten Prinzipien

Dekoratoren in TypeScript verstehen: Ein Ansatz nach den ersten Prinzipien

DDD
DDDOriginal
2024-09-21 06:29:021055Durchsuche

Understanding Decorators in TypeScript: A First-Principles Approach

Dekoratoren in TypeScript bieten einen leistungsstarken Mechanismus zum Ändern des Verhaltens von Klassen, Methoden, Eigenschaften und Parametern. Obwohl sie wie eine moderne Annehmlichkeit erscheinen mögen, basieren Dekoratoren auf dem etablierten Dekoratormuster, das in der objektorientierten Programmierung zu finden ist. Durch die Abstraktion allgemeiner Funktionen wie Protokollierung, Validierung oder Zugriffskontrolle ermöglichen Dekoratoren Entwicklern, saubereren und wartbareren Code zu schreiben.

In diesem Artikel werden wir Dekoratoren von Grund auf untersuchen, ihre Kernfunktionalität aufschlüsseln und sie von Grund auf implementieren. Unterwegs schauen wir uns einige reale Anwendungen an, die den Nutzen von Dekoratoren in der alltäglichen TypeScript-Entwicklung demonstrieren.

Was ist ein Dekorateur?

In TypeScript ist ein Dekorator einfach eine Funktion, die an eine Klasse, Methode, Eigenschaft oder einen Parameter angehängt werden kann. Diese Funktion wird zur Entwurfszeit ausgeführt und gibt Ihnen die Möglichkeit, das Verhalten oder die Struktur des Codes vor seiner Ausführung zu ändern. Dekoratoren ermöglichen Metaprogrammierung, sodass wir zusätzliche Funktionen hinzufügen können, ohne die ursprüngliche Logik zu ändern.

Beginnen wir mit einem einfachen Beispiel eines Methodendekorators, der protokolliert, wenn eine Methode aufgerufen wird:

function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = function (...args: any[]) {
    console.log(`Calling ${propertyKey} with arguments: ${args}`);
    return originalMethod.apply(this, args);
  };

  return descriptor;
}

class Example {
  @log
  greet(name: string) {
    return `Hello, ${name}`;
  }
}

const example = new Example();
example.greet('John');

Hier umschließt der Log-Dekorator die Greet-Methode und protokolliert ihren Aufruf und ihre Parameter, bevor er sie ausführt. Dieses Muster ist nützlich, um übergreifende Themen wie die Protokollierung von der Kernlogik zu trennen.

Wie Dekorateure arbeiten

Dekoratoren in TypeScript sind Funktionen, die Metadaten zu dem Element, das sie dekorieren, aufnehmen. Basierend auf diesen Metadaten (wie Klassenprototypen, Methodennamen oder Eigenschaftsdeskriptoren) können Dekorateure das Verhalten ändern oder sogar das dekorierte Objekt ersetzen.

Arten von Dekorateuren

Dekoratoren können auf verschiedene Ziele mit jeweils unterschiedlichen Zwecken angewendet werden:

  • Klassendekoratoren: Eine Funktion, die den Konstruktor der Klasse empfängt.
function classDecorator(constructor: Function) {
  // Modify or extend the class constructor or prototype
}
  • Methodendekoratoren: Eine Funktion, die das Zielobjekt, den Methodennamen und den Methodendeskriptor empfängt.
function methodDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  // Modify the method's descriptor
}
  • Property Decorators: Eine Funktion, die das Zielobjekt und den Eigenschaftsnamen empfängt.
function propertyDecorator(target: any, propertyKey: string) {
  // Modify the behavior of the property
}
  • Parameter-Dekoratoren: Eine Funktion, die das Ziel, den Methodennamen und den Index des Parameters empfängt.
function parameterDecorator(target: any, propertyKey: string, parameterIndex: number) {
  // Modify or inspect the method's parameter
}

Weitergabe von Argumenten an Dekorateure

Eine der mächtigsten Eigenschaften von Dekorateuren ist ihre Fähigkeit, Argumente anzunehmen, sodass Sie ihr Verhalten anpassen können. Erstellen wir beispielsweise einen Methodendekorator, der Methodenaufrufe bedingt auf der Grundlage eines Arguments protokolliert.

function logConditionally(shouldLog: boolean) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;

    descriptor.value = function (...args: any[]) {
      if (shouldLog) {
        console.log(`Calling ${propertyKey} with arguments: ${args}`);
      }
      return originalMethod.apply(this, args);
    };

    return descriptor;
  };
}

class Example {
  @logConditionally(true)
  greet(name: string) {
    return `Hello, ${name}`;
  }
}

const example = new Example();
example.greet('TypeScript Developer');

Durch die Übergabe von true an den logConditionally-Dekorator stellen wir sicher, dass die Methode ihre Ausführung protokolliert. Wenn wir false übergeben, wird die Protokollierung übersprungen. Diese Flexibilität ist der Schlüssel dazu, Dekorateure vielseitig und wiederverwendbar zu machen.

Reale Anwendungen von Dekorateuren

Dekoratoren haben in vielen Bibliotheken und Frameworks praktische Anwendung gefunden. Hier sind einige bemerkenswerte Beispiele, die veranschaulichen, wie Dekorateure komplexe Funktionen optimieren:

  • Validierung im Klassenvalidator: In datengesteuerten Anwendungen ist die Validierung von entscheidender Bedeutung. Das Paket „class-validator“ verwendet Dekoratoren, um den Prozess der Feldvalidierung in TypeScript-Klassen zu vereinfachen.
import { IsEmail, IsNotEmpty } from 'class-validator';

class User {
  @IsNotEmpty()
  name: string;

  @IsEmail()
  email: string;
}

In diesem Beispiel stellen die Dekoratoren @IsEmail und @IsNotEmpty sicher, dass das E-Mail-Feld eine gültige E-Mail-Adresse ist und das Namensfeld nicht leer ist. Diese Dekoratoren sparen Zeit, da keine manuelle Validierungslogik erforderlich ist.

  • Objektrelationale Zuordnung mit TypeORM: Dekoratoren werden häufig in ORM-Frameworks wie TypeORM verwendet, um TypeScript-Klassen Datenbanktabellen zuzuordnen. Diese Zuordnung erfolgt deklarativ mithilfe von Dekoratoren.
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  email: string;
}

Hier definieren @Entity, @Column und @PrimaryGeneratedColumn die Struktur der Benutzertabelle. Diese Dekoratoren abstrahieren die Komplexität der SQL-Tabellenerstellung und machen den Code dadurch lesbarer und wartbarer.

  • Angular Dependency Injection: In Angular spielen Dekorateure eine zentrale Rolle bei der Verwaltung von Diensten und Komponenten. Der @Injectable-Dekorator markiert eine Klasse als Dienst, der in andere Komponenten oder Dienste eingefügt werden kann.
@Injectable({
  providedIn: 'root',
})
class UserService {
  constructor(private http: HttpClient) {}
}

Der @Injectable-Dekorator signalisiert in diesem Fall dem Abhängigkeitsinjektionssystem von Angular, dass der UserService global bereitgestellt werden soll. Dies ermöglicht eine nahtlose Integration von Diensten in der gesamten Anwendung.

Implementierung eigener Dekorateure: Eine Aufschlüsselung

Dekorateure sind im Kern nur Funktionen. Lassen Sie uns den Prozess der Erstellung von Dekorateuren von Grund auf aufschlüsseln:

Class Decorator

A class decorator receives the constructor of the class and can be used to modify the class prototype or even replace the constructor.

function AddTimestamp(constructor: Function) {
  constructor.prototype.timestamp = new Date();
}

@AddTimestamp
class MyClass {
  id: number;
  constructor(id: number) {
    this.id = id;
  }
}

const instance = new MyClass(1);
console.log(instance.timestamp);  // Outputs the current timestamp

Method Decorator

A method decorator modifies the method descriptor, allowing you to alter the behavior of the method itself.

function logExecutionTime(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = function (...args: any[]) {
    const start = performance.now();
    const result = originalMethod.apply(this, args);
    const end = performance.now();
    console.log(`${propertyKey} executed in ${end - start}ms`);
    return result;
  };

  return descriptor;
}

class Service {
  @logExecutionTime
  execute() {
    // Simulate work
    for (let i = 0; i < 1e6; i++) {}
  }
}

const service = new Service();
service.execute();  // Logs the execution time

Property Decorator

A property decorator allows you to intercept property access and modification, which can be useful for tracking changes.

function trackChanges(target: any, propertyKey: string) {
  let value = target[propertyKey];

  const getter = () => value;
  const setter = (newValue: any) => {
    console.log(`${propertyKey} changed from ${value} to ${newValue}`);
    value = newValue;
  };

  Object.defineProperty(target, propertyKey, {
    get: getter,
    set: setter,
  });
}

class Product {
  @trackChanges
  price: number;

  constructor(price: number) {
    this.price = price;
  }
}

const product = new Product(100);
product.price = 200;  // Logs the change

Conclusion

Decorators in TypeScript allow you to abstract and reuse functionality in a clean, declarative manner. Whether you're working with validation, ORMs, or dependency injection, decorators help reduce boilerplate and keep your code modular and maintainable. Understanding how they work from first principles makes it easier to leverage their full potential and craft custom solutions tailored to your application.

By taking a deeper look at the structure and real-world applications of decorators, you've now seen how they can simplify complex coding tasks and streamline code across various domains.

Das obige ist der detaillierte Inhalt vonDekoratoren in TypeScript verstehen: Ein Ansatz nach den ersten Prinzipien. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn