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,則跳過日誌記錄。這種靈活性是使裝飾器具有多功能性和可重複使用性的關鍵。
裝飾器在許多庫和框架中都有實際用途。以下是一些值得注意的範例,說明了裝飾器如何簡化複雜的功能:
import { IsEmail, IsNotEmpty } from 'class-validator'; class User { @IsNotEmpty() name: string; @IsEmail() email: string; }
在此範例中,@IsEmail 和 @IsNotEmpty 裝飾器可確保電子郵件欄位是有效的電子郵件地址且名稱欄位不為空。這些裝飾器消除了手動驗證邏輯的需要,從而節省了時間。
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; @Entity() class User { @PrimaryGeneratedColumn() id: number; @Column() name: string; @Column() email: string; }
這裡,@Entity、@Column 和 @PrimaryGeneeratedColumn 定義了 User 表的結構。這些裝飾器抽象化了 SQL 表創建的複雜性,使程式碼更具可讀性和可維護性。
@Injectable({ providedIn: 'root', }) class UserService { constructor(private http: HttpClient) {} }
在這種情況下,@Injectable 裝飾器向 Angular 的依賴注入系統發出訊號,表示應在全域範圍內提供 UserService。這允許跨應用程式無縫整合服務。
裝飾器的核心就是函數。讓我們分解從頭開始創建裝飾器的過程:
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
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
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
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中文網其他相關文章!