TypeScript裝飾器:從入門到精通
什么是裝飾器?
裝飾器(Decorator)是TypeScript中一個非常酷的特性,它允許我們在不修改原有代碼的情況下,給類、方法、屬性等添加額外的功能。想象一下裝飾器就像給你的代碼"穿衣服",可以一層層地疊加功能,而不會破壞原有的結構。
在ES2016中,裝飾器還只是一個提案,但TypeScript已經提前實現了這個功能。要使用裝飾器,需要在tsconfig.json
中開啟experimentalDecorators
選項。
裝飾器基礎
類裝飾器
類裝飾器是最簡單的一種裝飾器,它接收一個構造函數作為參數。讓我們看個例子:
function logClass(target: Function) {console.log(`類 ${target.name} 被裝飾了`);
}@logClass
class MyClass {constructor() {console.log('創建MyClass實例');}
}const myClass = new MyClass();
// 輸出:
// 類 MyClass 被裝飾了
// 創建MyClass實例
方法裝飾器
方法裝飾器可以攔截方法的調用,非常適合用于日志記錄、權限驗證等場景:
function logMethod(target: any, propertyKey: string, descriptor: PropertyDescriptor) {const originalMethod = descriptor.value;descriptor.value = function(...args: any[]) {console.log(`調用方法 ${propertyKey},參數: ${JSON.stringify(args)}`);const result = originalMethod.apply(this, args);console.log(`方法 ${propertyKey} 返回: ${result}`);return result;};return descriptor;
}class Calculator {@logMethodadd(a: number, b: number): number {return a + b;}
}const calc = new Calculator();
calc.add(2, 3);
// 輸出:
// 調用方法 add,參數: [2,3]
// 方法 add 返回: 5
裝飾器工廠
有時候我們希望裝飾器能接收參數,這時候就需要使用裝飾器工廠:
function logWithPrefix(prefix: string) {return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {const originalMethod = descriptor.value;descriptor.value = function(...args: any[]) {console.log(`[${prefix}] 調用方法 ${propertyKey}`);return originalMethod.apply(this, args);};return descriptor;};
}class Logger {@logWithPrefix('DEBUG')log(message: string) {console.log(message);}
}const logger = new Logger();
logger.log('這是一條日志');
// 輸出:
// [DEBUG] 調用方法 log
// 這是一條日志
裝飾器的執行順序
當多個裝飾器應用于同一個聲明時,它們的執行順序很重要:
- 參數裝飾器,然后是方法、訪問器或屬性裝飾器
- 裝飾器從最靠近聲明的裝飾器開始執行
- 類裝飾器最后執行
function first() {console.log('first() 工廠函數');return function(target: any) {console.log('first() 裝飾器');};
}function second() {console.log('second() 工廠函數');return function(target: any) {console.log('second() 裝飾器');};
}@first()
@second()
class ExampleClass {}// 輸出:
// first() 工廠函數
// second() 工廠函數
// second() 裝飾器
// first() 裝飾器
裝飾器的實際應用
1. 自動綁定this
在React類組件中,我們經常需要綁定方法的this指向,裝飾器可以幫我們自動完成:
function autobind(_: any, _2: string, descriptor: PropertyDescriptor) {const originalMethod = descriptor.value;const adjDescriptor: PropertyDescriptor = {configurable: true,enumerable: false,get() {const boundFn = originalMethod.bind(this);return boundFn;}};return adjDescriptor;
}class Button {@autobindonClick() {console.log('按鈕被點擊', this);}
}const button = new Button();
document.addEventListener('click', button.onClick);
2. 表單驗證
裝飾器可以很方便地實現表單驗證邏輯:
function validate(min: number, max: number) {return function(target: any, propertyKey: string) {let value: number;const getter = function() {return value;};const setter = function(newVal: number) {if (newVal < min || newVal > max) {throw new Error(`值必須在 ${min} 和 ${max} 之間`);}value = newVal;};Object.defineProperty(target, propertyKey, {get: getter,set: setter});};
}class User {@validate(1, 120)age: number;
}const user = new User();
user.age = 25; // 正常
user.age = 0; // 拋出錯誤
裝飾器的局限性
雖然裝飾器很強大,但也有一些限制:
- 不能裝飾函數聲明(只能裝飾類、方法、訪問器、屬性或參數)
- 裝飾器不能修改類的結構(比如不能添加或刪除類成員)
- 目前還是實驗性特性,未來可能會有變化
總結
TypeScript裝飾器是一個非常強大的元編程工具,它允許我們以聲明式的方式為代碼添加功能。通過本文的介紹,你應該已經掌握了:
- 裝飾器的基本概念和使用方法
- 類裝飾器、方法裝飾器、屬性裝飾器的區別
- 如何創建裝飾器工廠
- 裝飾器的執行順序
- 裝飾器在實際項目中的應用場景
裝飾器在Angular、NestJS等框架中都有廣泛應用,掌握好這個特性,能讓你的代碼更加優雅和可維護。