TypeScript装饰器原理
- 本质:装饰器是一种特殊类型的声明,它能够被附加到类声明、方法、属性或参数上。本质上,它们是函数,在运行时对目标对象进行元编程。
- 执行时机:在类的声明和成员被评估时执行。例如,类装饰器在类定义被加载时执行,方法装饰器在方法定义时执行。
创建方法
- 类装饰器
function classDecorator(target: Function) {
// 这里的target就是被装饰的类的构造函数
console.log('Class Decorator:', target.name);
return class extends target {
newProperty = 'new property added by decorator';
constructor(...args: any[]) {
super(...args);
console.log('New property added:', this.newProperty);
}
};
}
@classDecorator
class MyClass {}
- 方法装饰器
function methodDecorator(target: Object, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log('Before method execution');
const result = originalMethod.apply(this, args);
console.log('After method execution');
return result;
};
return descriptor;
}
class MyClass2 {
@methodDecorator
myMethod() {
console.log('Method is running');
}
}
- 属性装饰器
function propertyDecorator(target: Object, propertyKey: string) {
let value: any;
const getter = function() {
return value;
};
const setter = function(newValue: any) {
console.log('Setting property value:', newValue);
value = newValue;
};
if (delete target[propertyKey]) {
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
}
}
class MyClass3 {
@propertyDecorator
myProperty: string;
}
- 参数装饰器
function parameterDecorator(target: Object, propertyKey: string, parameterIndex: number) {
console.log(`Parameter decorator on ${propertyKey} at index ${parameterIndex}`);
}
class MyClass4 {
myMethod(@parameterDecorator param: string) {
console.log('Method with parameter:', param);
}
}
在大型前端项目架构设计中的应用
- 权限控制
function requirePermission(permission: string) {
return function(target: Object, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
// 这里模拟权限检查逻辑
const hasPermission = checkPermission(permission);
if (hasPermission) {
return originalMethod.apply(this, args);
} else {
throw new Error('Permission denied');
}
};
return descriptor;
};
}
function checkPermission(permission: string): boolean {
// 实际逻辑中从存储或API获取权限信息
return true;
}
class AdminPanel {
@requirePermission('admin:view')
viewDashboard() {
console.log('Viewing dashboard');
}
}
- 日志记录
function log(target: Object, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`Calling method ${propertyKey} with args:`, args);
const result = originalMethod.apply(this, args);
console.log(`Method ${propertyKey} returned:`, result);
return result;
};
return descriptor;
}
class Calculator {
@log
add(a: number, b: number) {
return a + b;
}
}
- 依赖注入
const inject = <T>(dependency: T) => (target: Object, propertyKey: string) => {
Object.defineProperty(target, propertyKey, {
value: dependency,
enumerable: true,
configurable: true
});
};
class DatabaseService {
connect() {
console.log('Connected to database');
}
}
class UserService {
@inject(new DatabaseService())
database: DatabaseService;
getUser() {
this.database.connect();
console.log('Getting user');
}
}
潜在问题及解决方案
- 兼容性问题:装饰器是ES7的提案,目前在一些环境中可能不支持。
- 解决方案:使用Babel等工具进行转译,将装饰器代码转换为ES5或ES6代码,以确保广泛的兼容性。
- 性能问题:由于装饰器在运行时执行,可能会影响性能,特别是在频繁调用的方法上使用装饰器。
- 解决方案:尽量避免在性能敏感的代码路径上使用装饰器。或者对装饰器逻辑进行优化,减少不必要的计算。
- 调试困难:装饰器的嵌套和复杂逻辑可能导致调试困难。
- 解决方案:在装饰器内部添加详细的日志记录,使用调试工具(如Chrome DevTools)来跟踪装饰器的执行流程。同时,尽量保持装饰器逻辑简单,易于理解和调试。