首頁  >  文章  >  web前端  >  理解 TypeScript 中的裝飾器:第一原理方法

理解 TypeScript 中的裝飾器:第一原理方法

DDD
DDD原創
2024-09-21 06:29:021029瀏覽

Understanding Decorators in TypeScript: A First-Principles Approach

TypeScript 中的裝飾器提供了一個強大的機制來修改類別、方法、屬性和參數的行為。雖然它們看起來像是一種現代的便利,但裝飾器植根於物件導向程式設計中成熟的裝飾器模式。透過抽像日誌記錄、驗證或存取控制等常見功能,裝飾器可讓開發人員編寫更清晰、更易於維護的程式碼。

在本文中,我們將從首要原則探索裝飾器,分解其核心功能,並從頭開始實現它們。在此過程中,我們將了解一些實際應用程序,這些應用程式展示了裝飾器在日常 TypeScript 開發中的實用性。

什麼是裝飾器?

在 TypeScript 中,裝飾器只是一個可以附加到類別、方法、屬性或參數的函數。此函數在設計時執行,使您能夠在程式碼運行之前更改程式碼的行為或結構。裝飾器支援元編程,允許我們在不修改原始邏輯的情況下添加額外的功能。

讓我們從一個方法裝飾器的簡單範例開始,該範例記錄呼叫方法的時間:

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');

這裡,日誌裝飾器包裝了greet方法,在執行之前記錄其呼叫和參數。此模式對於將日誌記錄等橫切關注點與核心邏輯分開非常有用。

裝飾器如何運作

TypeScript 中的裝飾器是接受與其所裝飾的項目相關的元資料的函數。基於這些元資料(如類別原型、方法名稱或屬性描述符),裝飾器可以修改行為甚至替換被裝飾的物件。

裝飾器的類型

裝飾器可以應用在各種目標,每個目標都有不同的目的:

  • 類別裝飾器:接收類別建構子的函數。
function classDecorator(constructor: Function) {
  // Modify or extend the class constructor or prototype
}
  • 方法裝飾器:接收目標物件、方法名稱、方法描述符的函數。
function methodDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  // Modify the method's descriptor
}
  • 屬性裝飾器:接收目標物件和屬性名稱的函數。
function propertyDecorator(target: any, propertyKey: string) {
  // Modify the behavior of the property
}
  • 參數裝飾器:接收目標、方法名稱、參數索引的函數。
function parameterDecorator(target: any, propertyKey: string, parameterIndex: number) {
  // Modify or inspect the method's parameter
}

將參數傳遞給裝飾器

裝飾器最強大的功能之一是它們接受參數的能力,允許您自訂它們的行為。例如,讓我們建立一個方法裝飾器,它根據參數有條件地記錄方法呼叫。

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');

透過將 true 傳遞給 logConditionally 裝飾器,我們確保方法記錄其執行情況。如果我們傳遞 false,則跳過日誌記錄。這種靈活性是使裝飾器具有多功能性和可重複使用性的關鍵。

裝飾器的實際應用

裝飾器在許多庫和框架中都有實際用途。以下是一些值得注意的範例,說明了裝飾器如何簡化複雜的功能:

  • 類別驗證器中的驗證:在資料驅動的應用程式中,驗證至關重要。 class-validator 套件使用裝飾器來簡化驗證 TypeScript 類別中欄位的過程。
import { IsEmail, IsNotEmpty } from 'class-validator';

class User {
  @IsNotEmpty()
  name: string;

  @IsEmail()
  email: string;
}

在此範例中,@IsEmail 和 @IsNotEmpty 裝飾器可確保電子郵件欄位是有效的電子郵件地址且名稱欄位不為空。這些裝飾器消除了手動驗證邏輯的需要,從而節省了時間。

  • 使用 TypeORM 進行物件關聯映射:裝飾器廣泛用於 TypeORM 等 ORM 框架中,用於將 TypeScript 類別對應到資料庫表。此映射是使用裝飾器以聲明方式完成的。
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

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

  @Column()
  name: string;

  @Column()
  email: string;
}

這裡,@Entity、@Column 和 @PrimaryGeneeratedColumn 定義了 User 表的結構。這些裝飾器抽象化了 SQL 表創建的複雜性,使程式碼更具可讀性和可維護性。

  • Angular 依賴注入:在 Angular 中,裝飾器在管理服務和組件方面發揮關鍵作用。 @Injectable 裝飾器將一個類別標記為可以注入其他元件或服務的服務。
@Injectable({
  providedIn: 'root',
})
class UserService {
  constructor(private http: HttpClient) {}
}

在這種情況下,@Injectable 裝飾器向 Angular 的依賴注入系統發出訊號,表示應在全域範圍內提供 UserService。這允許跨應用程式無縫整合服務。

實現你自己的裝飾器:分解

裝飾器的核心就是函數。讓我們分解從頭開始創建裝飾器的過程:

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.

以上是理解 TypeScript 中的裝飾器:第一原理方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn