装饰器大全

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 中装饰器的强大功能,可以用于日志、缓存、验证、依赖注入等各种场景,大大提高了代码的可重用性和可维护性。

posted @ 2025-10-14 22:35  阿木隆1237  阅读(16)  评论(0)    收藏  举报