<返回目录     Powered by claud/xia兄

第11课: 装饰器Decorator

什么是装饰器?

装饰器是一种特殊的声明,可以附加到类、方法、访问器、属性或参数上。装饰器使用 @expression 形式,是一种元编程的方式。

启用装饰器:
// tsconfig.json
{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

类装饰器

// 类装饰器函数
function sealed(constructor: Function) {
  Object.seal(constructor);
  Object.seal(constructor.prototype);
}

@sealed
class Greeter {
  greeting: string;
  constructor(message: string) {
    this.greeting = message;
  }
}

// 装饰器工厂
function classDecorator(value: string) {
  return function(constructor: Function) {
    console.log(`Class decorator: ${value}`);
  };
}

@classDecorator("test")
class MyClass {}

方法装饰器

function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = function(...args: any[]) {
    console.log(`Calling ${propertyKey} with args:`, args);
    const result = originalMethod.apply(this, args);
    console.log(`Result:`, result);
    return result;
  };

  return descriptor;
}

class Calculator {
  @log
  add(a: number, b: number): number {
    return a + b;
  }
}

const calc = new Calculator();
calc.add(2, 3);
// 输出:
// Calling add with args: [2, 3]
// Result: 5

访问器装饰器

function configurable(value: boolean) {
  return function(
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ) {
    descriptor.configurable = value;
  };
}

class Point {
  private _x: number;
  private _y: number;

  constructor(x: number, y: number) {
    this._x = x;
    this._y = y;
  }

  @configurable(false)
  get x() {
    return this._x;
  }

  @configurable(false)
  get y() {
    return this._y;
  }
}

属性装饰器

function format(formatString: string) {
  return function(target: any, propertyKey: string) {
    let value: string;

    const getter = function() {
      return value;
    };

    const setter = function(newVal: string) {
      value = `${formatString} ${newVal}`;
    };

    Object.defineProperty(target, propertyKey, {
      get: getter,
      set: setter,
      enumerable: true,
      configurable: true
    });
  };
}

class Greeter {
  @format("Hello,")
  greeting: string;
}

const greeter = new Greeter();
greeter.greeting = "World";
console.log(greeter.greeting); // "Hello, World"

参数装饰器

function required(target: any, propertyKey: string, parameterIndex: number) {
  const existingRequiredParameters: number[] =
    Reflect.getOwnMetadata("required", target, propertyKey) || [];
  existingRequiredParameters.push(parameterIndex);
  Reflect.defineMetadata("required", existingRequiredParameters, target, propertyKey);
}

function validate(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const method = descriptor.value;

  descriptor.value = function(...args: any[]) {
    const requiredParameters: number[] =
      Reflect.getOwnMetadata("required", target, propertyKey) || [];

    for (const parameterIndex of requiredParameters) {
      if (parameterIndex >= args.length || args[parameterIndex] === undefined) {
        throw new Error("Missing required argument");
      }
    }

    return method.apply(this, args);
  };
}

class Greeter {
  @validate
  greet(@required name: string) {
    return `Hello, ${name}`;
  }
}

装饰器组合

多个装饰器可以应用到同一个声明上:

function first() {
  console.log("first(): factory evaluated");
  return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log("first(): called");
  };
}

function second() {
  console.log("second(): factory evaluated");
  return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log("second(): called");
  };
}

class ExampleClass {
  @first()
  @second()
  method() {}
}

// 输出顺序:
// first(): factory evaluated
// second(): factory evaluated
// second(): called
// first(): called

实际应用示例

// 性能监控装饰器
function measure(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = async function(...args: any[]) {
    const start = performance.now();
    const result = await originalMethod.apply(this, args);
    const end = performance.now();
    console.log(`${propertyKey} took ${end - start}ms`);
    return result;
  };

  return descriptor;
}

// 缓存装饰器
function memoize(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;
  const cache = new Map();

  descriptor.value = function(...args: any[]) {
    const key = JSON.stringify(args);
    if (cache.has(key)) {
      return cache.get(key);
    }
    const result = originalMethod.apply(this, args);
    cache.set(key, result);
    return result;
  };

  return descriptor;
}

// 权限检查装饰器
function authorize(role: string) {
  return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;

    descriptor.value = function(...args: any[]) {
      if (!checkUserRole(role)) {
        throw new Error("Unauthorized");
      }
      return originalMethod.apply(this, args);
    };

    return descriptor;
  };
}

class UserService {
  @measure
  @memoize
  async getUser(id: number) {
    // 获取用户数据
  }

  @authorize("admin")
  deleteUser(id: number) {
    // 删除用户
  }
}
装饰器的应用场景:
练习:
  1. 创建一个类装饰器,为类添加时间戳属性
  2. 实现一个方法装饰器,记录方法执行时间
  3. 创建一个属性装饰器,验证属性值的范围
  4. 组合多个装饰器,实现复杂功能