装饰器简介

装饰器是 TypeScript 的一项实验性功能,提供了一种声明式的方式来修改类和类成员。装饰器基于 ES7 提案,在 Angular、NestJS 等框架中广泛使用。

装饰器的本质

装饰器本质上是一个函数,在编译时被调用。装饰器接收不同的参数,取决于装饰的目标类型。

function myDecorator(target: any) {
  console.log("装饰器被调用")
}

@myDecorator
class MyClass {}

装饰器在类定义时立即执行,而不是在实例化时。

启用装饰器

使用装饰器需要在 tsconfig.json 中启用实验性功能。

{
  "compilerOptions": {
    "target": "ES5",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

experimentalDecorators 启用装饰器语法,emitDecoratorMetadata 启用装饰器元数据。

装饰器的分类

TypeScript 装饰器分为以下几类:

装饰器类型应用目标参数
类装饰器构造函数
方法装饰器方法target, propertyKey, descriptor
属性装饰器属性target, propertyKey
参数装饰器参数target, propertyKey, parameterIndex
访问器装饰器getter/settertarget, propertyKey, descriptor

装饰器工厂

装饰器工厂是一个返回装饰器的函数,可以接收参数。

function log(prefix: string) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const original = descriptor.value
    descriptor.value = function (...args: any[]) {
      console.log(`${prefix}: 调用 ${propertyKey}`)
      return original.apply(this, args)
    }
    return descriptor
  }
}

class Calculator {
  @log("计算器")
  add(a: number, b: number): number {
    return a + b
  }
}

const calc = new Calculator()
calc.add(1, 2)

装饰器组合

多个装饰器可以同时应用于一个声明。

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 MyClass {}

执行顺序:工厂函数从上到下,装饰器从下到上。

装饰器的用途

装饰器常用于以下场景:

  • 日志记录:自动记录方法调用
  • 性能监控:测量方法执行时间
  • 缓存:缓存方法返回值
  • 验证:验证参数或属性
  • 权限控制:检查用户权限
  • 依赖注入:自动注入依赖

简单示例

一个简单的日志装饰器。

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

  descriptor.value = function (...args: any[]) {
    console.log(`调用 ${propertyKey}`)
    console.log(`参数: ${JSON.stringify(args)}`)
    const result = original.apply(this, args)
    console.log(`返回: ${JSON.stringify(result)}`)
    return result
  }

  return descriptor
}

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

  @log
  multiply(a: number, b: number): number {
    return a * b
  }
}

const service = new MathService()
service.add(1, 2)
service.multiply(3, 4)

小结

装饰器是一种声明式的方式,用于修改类和类成员的行为。装饰器本质上是一个函数,在编译时被调用。装饰器分为类装饰器、方法装饰器、属性装饰器、参数装饰器和访问器装饰器。装饰器工厂可以接收参数并返回装饰器。装饰器常用于日志、缓存、验证、权限控制等场景。