装饰器大全
JavaScript/TypeScript 装饰器实现详解
1. 装饰器基础
1.1 装饰器类型
// 类装饰器
function ClassDecorator(constructor: Function) {
console.log('类装饰器执行');
}
// 方法装饰器
function MethodDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.log('方法装饰器执行');
}
// 属性装饰器
function PropertyDecorator(target: any, propertyKey: string) {
console.log('属性装饰器执行');
}
// 参数装饰器
function ParameterDecorator(target: any, propertyKey: string, parameterIndex: number) {
console.log('参数装饰器执行');
}
2. 类装饰器
2.1 基础类装饰器
// 简单的类装饰器 - 添加元数据
function Component(name: string) {
return function<T extends { new(...args: any[]): {} }>(constructor: T) {
return class extends constructor {
componentName = name;
version = '1.0.0';
// 重写 toString 方法
toString() {
return `Component: ${this.componentName} (v${this.version})`;
}
};
};
}
@Component('UserProfile')
class UserProfile {
name: string;
constructor(name: string) {
this.name = name;
}
}
const profile = new UserProfile('张三');
console.log(profile.toString()); // "Component: UserProfile (v1.0.0)"
2.2 带参数的类装饰器
// 单例装饰器
function Singleton<T extends { new(...args: any[]): {} }>(constructor: T) {
let instance: T;
return class extends constructor {
constructor(...args: any[]) {
if (!instance) {
super(...args);
instance = this as any;
}
return instance;
}
};
}
@Singleton
class DatabaseConnection {
private connection: string;
constructor() {
this.connection = 'Connected to database';
console.log('创建数据库连接');
}
query(sql: string) {
return `执行: ${sql}`;
}
}
const db1 = new DatabaseConnection(); // "创建数据库连接"
const db2 = new DatabaseConnection(); // 不会再次创建
console.log(db1 === db2); // true
2.3 混合类装饰器
// 可序列化混合
function Serializable<T extends { new(...args: any[]): {} }>(constructor: T) {
return class extends constructor {
serialize(): string {
return JSON.stringify(this);
}
deserialize(data: string): void {
const obj = JSON.parse(data);
Object.assign(this, obj);
}
};
}
// 时间戳混合
function Timestampable<T extends { new(...args: any[]): {} }>(constructor: T) {
return class extends constructor {
createdAt: Date = new Date();
updatedAt: Date = new Date();
updateTimestamp() {
this.updatedAt = new Date();
}
};
}
@Serializable
@Timestampable
class Product {
constructor(
public name: string,
public price: number
) {}
}
const product = new Product('iPhone', 5999);
console.log(product.serialize()); // 序列化输出
product.updateTimestamp();
3. 方法装饰器
3.1 日志装饰器
// 方法执行日志
function Log(target: any, propertyName: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`调用方法: ${propertyName}`);
console.log(`参数:`, args);
const result = originalMethod.apply(this, args);
console.log(`返回值:`, result);
return result;
};
return descriptor;
}
class Calculator {
@Log
add(a: number, b: number): number {
return a + b;
}
@Log
multiply(a: number, b: number): number {
return a * b;
}
}
const calc = new Calculator();
calc.add(2, 3); // 会输出调用日志
3.2 缓存装饰器
// 方法结果缓存
function Cache(timeout: number = 5000) {
const cache = new Map<string, { value: any; timestamp: number }>();
return function(target: any, propertyName: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
const key = `${propertyName}_${JSON.stringify(args)}`;
const cached = cache.get(key);
// 检查缓存是否有效
if (cached && Date.now() - cached.timestamp < timeout) {
console.log(`缓存命中: ${propertyName}`);
return cached.value;
}
// 执行原方法并缓存结果
const result = originalMethod.apply(this, args);
cache.set(key, {
value: result,
timestamp: Date.now()
});
return result;
};
return descriptor;
};
}
class ApiService {
@Cache(10000) // 缓存10秒
async fetchUser(id: number): Promise<any> {
console.log(`调用API获取用户 ${id}`);
// 模拟API调用
await new Promise(resolve => setTimeout(resolve, 1000));
return { id, name: `用户${id}` };
}
}
const api = new ApiService();
await api.fetchUser(1); // 调用API
await api.fetchUser(1); // 从缓存获取
3.3 防抖装饰器
// 防抖装饰器
function Debounce(delay: number = 300) {
let timeoutId: NodeJS.Timeout;
return function(target: any, propertyName: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
clearTimeout(timeoutId);
return new Promise((resolve) => {
timeoutId = setTimeout(() => {
resolve(originalMethod.apply(this, args));
}, delay);
});
};
return descriptor;
};
}
class SearchService {
@Debounce(500)
search(query: string): Promise<string[]> {
console.log(`搜索: ${query}`);
// 模拟搜索
return Promise.resolve([`${query}结果1`, `${query}结果2`]);
}
}
3.4 重试装饰器
// 自动重试装饰器
function Retry(maxRetries: number = 3, delay: number = 1000) {
return function(target: any, propertyName: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = async function(...args: any[]) {
let lastError: Error;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await originalMethod.apply(this, args);
} catch (error) {
lastError = error as Error;
console.warn(`方法 ${propertyName} 第 ${attempt} 次失败:`, error);
if (attempt < maxRetries) {
console.log(`等待 ${delay}ms 后重试...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
throw lastError!;
};
return descriptor;
};
}
class UnstableService {
private callCount = 0;
@Retry(3, 1000)
async unstableOperation(): Promise<string> {
this.callCount++;
if (this.callCount < 3) {
throw new Error('临时故障');
}
return '操作成功';
}
}
4. 属性装饰器
4.1 属性验证装饰器
// 属性验证装饰器
function Validate(rules: {
required?: boolean;
minLength?: number;
maxLength?: number;
pattern?: RegExp;
}) {
return function(target: any, propertyName: string) {
let value: any;
const getter = function() {
return value;
};
const setter = function(newValue: any) {
// 必填验证
if (rules.required && (newValue === null || newValue === undefined || newValue === '')) {
throw new Error(`${propertyName} 是必填字段`);
}
// 长度验证
if (typeof newValue === 'string') {
if (rules.minLength && newValue.length < rules.minLength) {
throw new Error(`${propertyName} 长度不能少于 ${rules.minLength}`);
}
if (rules.maxLength && newValue.length > rules.maxLength) {
throw new Error(`${propertyName} 长度不能超过 ${rules.maxLength}`);
}
}
// 正则验证
if (rules.pattern && typeof newValue === 'string' && !rules.pattern.test(newValue)) {
throw new Error(`${propertyName} 格式不正确`);
}
value = newValue;
};
// 重新定义属性
Object.defineProperty(target, propertyName, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
};
}
class User {
@Validate({ required: true, minLength: 2, maxLength: 50 })
name: string;
@Validate({ pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/ })
email: string;
constructor(name: string, email: string) {
this.name = name;
this.email = email;
}
}
const user = new User('张三', 'zhang@example.com');
// user.name = ''; // 会抛出错误
4.2 属性监听装饰器
// 属性变化监听
function Watch(callback: (newValue: any, oldValue: any) => void) {
return function(target: any, propertyName: string) {
let value: any;
const getter = function() {
return value;
};
const setter = function(newValue: any) {
const oldValue = value;
value = newValue;
callback(newValue, oldValue);
};
Object.defineProperty(target, propertyName, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
};
}
class FormModel {
@Watch((newVal, oldVal) => {
console.log(`name 从 "${oldVal}" 变为 "${newVal}"`);
})
name: string = '';
@Watch((newVal, oldVal) => {
console.log(`email 从 "${oldVal}" 变为 "${newVal}"`);
})
email: string = '';
}
const form = new FormModel();
form.name = '李四'; // 输出变化日志
5. 参数装饰器
5.1 参数验证装饰器
// 参数验证
function ValidateParam(rules: {
required?: boolean;
type?: 'string' | 'number' | 'boolean';
min?: number;
max?: number;
}) {
return function(target: any, propertyName: string, parameterIndex: number) {
// 存储验证规则到元数据
const existingValidations = Reflect.getMetadata('validations', target, propertyName) || [];
existingValidations.push({ parameterIndex, rules });
Reflect.defineMetadata('validations', existingValidations, target, propertyName);
};
}
// 方法装饰器来应用参数验证
function ValidateArgs(target: any, propertyName: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
const validations = Reflect.getMetadata('validations', target, propertyName) || [];
descriptor.value = function(...args: any[]) {
// 验证参数
for (const validation of validations) {
const { parameterIndex, rules } = validation;
const value = args[parameterIndex];
if (rules.required && (value === null || value === undefined)) {
throw new Error(`参数 ${parameterIndex} 是必需的`);
}
if (rules.type && typeof value !== rules.type) {
throw new Error(`参数 ${parameterIndex} 必须是 ${rules.type} 类型`);
}
if (rules.min !== undefined && value < rules.min) {
throw new Error(`参数 ${parameterIndex} 不能小于 ${rules.min}`);
}
if (rules.max !== undefined && value > rules.max) {
throw new Error(`参数 ${parameterIndex} 不能大于 ${rules.max}`);
}
}
return originalMethod.apply(this, args);
};
return descriptor;
}
class MathService {
@ValidateArgs
divide(
@ValidateParam({ required: true, type: 'number' })
a: number,
@ValidateParam({ required: true, type: 'number', min: 1 })
b: number
): number {
return a / b;
}
}
const math = new MathService();
// math.divide(10, 0); // 会抛出错误
6. 装饰器工厂和组合
6.1 装饰器组合
// 组合多个装饰器
function composeDecorators(...decorators: Function[]) {
return function(target: any, propertyKey?: string | symbol, descriptor?: PropertyDescriptor) {
for (let i = decorators.length - 1; i >= 0; i--) {
const decorator = decorators[i];
if (propertyKey === undefined) {
// 类装饰器
target = decorator(target) || target;
} else if (descriptor === undefined) {
// 属性装饰器
decorator(target, propertyKey);
} else {
// 方法装饰器或参数装饰器
const result = decorator(target, propertyKey, descriptor);
if (result) descriptor = result;
}
}
};
}
// 使用组合装饰器
const LogAndCache = composeDecorators(Log, Cache(5000));
class DataService {
@LogAndCache
async getData(id: number) {
return { id, data: `数据${id}` };
}
}
6.2 条件装饰器
// 条件装饰器
function Conditional(condition: boolean, trueDecorator: Function, falseDecorator?: Function) {
return function(...args: any[]) {
const decorator = condition ? trueDecorator : (falseDecorator || (() => {}));
return decorator(...args);
};
}
const isDevelopment = process.env.NODE_ENV === 'development';
class App {
@Conditional(isDevelopment, Log)
debugMethod() {
// 只在开发环境记录日志
}
@Conditional(!isDevelopment, Cache(30000))
productionMethod() {
// 在生产环境缓存
}
}
7. 元数据装饰器
7.1 使用 Reflect Metadata
import 'reflect-metadata';
// 定义元数据键
const DESIGN_TYPE = 'design:type';
const DESIGN_PARAMTYPES = 'design:paramtypes';
const DESIGN_RETURNTYPE = 'design:returntype';
// 自定义元数据装饰器
function Injectable() {
return function<T extends { new(...args: any[]): {} }>(constructor: T) {
// 注册到容器
Container.register(constructor);
return constructor;
};
}
function Inject(token: string) {
return function(target: any, propertyKey: string | symbol, parameterIndex: number) {
// 存储依赖注入信息
const existingInjections = Reflect.getMetadata('injections', target) || [];
existingInjections.push({ parameterIndex, token });
Reflect.defineMetadata('injections', existingInjections, target);
};
}
@Injectable()
class UserService {
getUsers() {
return ['用户1', '用户2'];
}
}
@Injectable()
class AppController {
constructor(
@Inject('UserService') private userService: UserService
) {}
getUsers() {
return this.userService.getUsers();
}
}
// 简单的依赖注入容器
class Container {
private static instances = new Map();
static register<T>(constructor: { new(...args: any[]): T }) {
this.instances.set(constructor, null);
}
static get<T>(constructor: { new(...args: any[]): T }): T {
if (!this.instances.has(constructor)) {
throw new Error(`未注册的类型: ${constructor.name}`);
}
if (!this.instances.get(constructor)) {
const injections = Reflect.getMetadata('injections', constructor) || [];
const args = injections.map((injection: any) => this.get(injection.token));
this.instances.set(constructor, new constructor(...args));
}
return this.instances.get(constructor);
}
}
8. 实际应用场景
8.1 Vue 组件装饰器
// Vue 3 组件装饰器
function Component(options: any) {
return function<T extends { new(...args: any[]): {} }>(constructor: T) {
return class extends constructor {
static __vccOpts = options;
};
};
}
function Prop(defaultValue?: any) {
return function(target: any, propertyKey: string) {
const constructor = target.constructor;
const props = constructor.__vccOpts?.props || {};
props[propertyKey] = {
type: Reflect.getMetadata('design:type', target, propertyKey),
default: defaultValue
};
constructor.__vccOpts = {
...constructor.__vccOpts,
props
};
};
}
@Component({
name: 'UserCard'
})
class UserCard extends Vue {
@Prop() name!: string;
@Prop(18) age!: number;
get displayText() {
return `${this.name} (${this.age}岁)`;
}
}
8.2 Express 路由装饰器
// Express 控制器装饰器
const routes: any[] = [];
function Controller(prefix: string = '') {
return function<T extends { new(...args: any[]): {} }>(constructor: T) {
return class extends constructor {
static prefix = prefix;
static routes = routes.filter(route => route.target === constructor.name);
};
};
}
function Get(path: string = '') {
return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
routes.push({
method: 'get',
path,
handler: propertyKey,
target: target.constructor.name
});
};
}
function Post(path: string = '') {
return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
routes.push({
method: 'post',
path,
handler: propertyKey,
target: target.constructor.name
});
};
}
@Controller('/api/users')
class UserController {
@Get('/')
getUsers() {
return { users: ['张三', '李四'] };
}
@Post('/')
createUser() {
return { message: '用户创建成功' };
}
}
// 注册路由
function registerRoutes(app: any) {
for (const route of routes) {
const controller = Container.get(UserController);
app[route.method](`${UserController.prefix}${route.path}`,
controller[route.handler].bind(controller));
}
}
这些装饰器实现展示了 JavaScript/TypeScript 中装饰器的强大功能,可以用于日志、缓存、验证、依赖注入等各种场景,大大提高了代码的可重用性和可维护性。
挣钱养家

浙公网安备 33010602011771号