TypeScript的装饰器

245 阅读3分钟

一、概述

1、装饰器定义

  • 它是一个表达式
  • 该表达式被执行后,返回一个函数
  • 函数的入参分别为 target、name 和 descriptor
  • 执行该函数后,可能返回 descriptor 对象,用于配置 target 对象

2、装饰器分类

1)根据装饰器的位置

  • 类装饰器
  • 函数装饰器
  • 属性装饰器
  • 参数装饰器 2)根据装饰器是否有参数
  • 无参装饰器(一般装饰器)
  • 有参装饰器(装饰器工厂)

二、装饰器的参数说明及应用

1. 类装饰器

类装饰声明

declare type ClassDecorator = <TFunction extends Function>(
  target: TFunction
) => TFunction | void;

参数说明: target: Object - 被装饰的类

示例1:无参数

function logClass (target: any) {
    console.log('hello world')
}

@logClass
class Greeting {
    constructor() {}
}

示例2: 有参数

function logClass (params: any) {
    return function (target: any) {
      target.prototype.say = function () {
        console.log(params)
      }
    }
}

@logClass('hello ts')
class Greeting {
    constructor() {}
}
var greet: any = new Greeting()
greet.say()

2. 函数装饰器

方法装饰声明

declare type MethodsDecorator = (target:Object, propertyKey: string | symbol ) => void;

方法装饰器顾名思义,用来装饰类的方法,参数说明:

  • target: Object - 被装饰的类
  • propertyKey: string | symbol - 方法名
  • descriptor: TypePropertyDescript - 属性描述符

示例:将类中的方法的参数类型全部转化成string类型

function logMethod (target: any, methodName: any, desc: any) {
    const methods = desc.value // 保存当前方法
    desc.value = function (...args: any[]) {
      args = args.map(item => String(item))
      methods.apply(this, args)
    }
  }

class Greeting {
    userName: string | undefined

    @logMethod
    getData (name: string, age: number) {
      console.log('change after:', typeof name, typeof age)
    }
}
var greet = new Greeting()
greet.getData('张三', 30)

3. 属性装饰器

属性装饰声明

declare type PropertyDecorator = (target:Object, propertyKey: string | symbol ) => void;

属性装饰器顾名思义,用来装饰类的属性。它接收两个参数

  • target: Object - 被装饰的类
  • propertyKey: string | symbol - 被装饰类的属性名

示例:给类的中属性设置指定一个默认值

function logProperty (params: any) {
    return function (target: any, propertyName: any) {
      target[propertyName] = params
    }
  }

 class Greeting {
    @logProperty('张三')
    userName: string | undefined
    
    getData () {
      return this.userName
    }
 }
 var greet: any = new Greeting()
 console.log(greet.userName) // 张三

4. 参数装饰器

参数装饰器声明:

declare type ParameterDecorator = (target: Object, propertyKey: string | symbol, 
  parameterIndex: number ) => void

参数装饰器顾名思义,是用来装饰函数参数,它接收三个参数:

  • target: Object - 被装饰的类
  • propertyKey: string | symbol - 方法名
  • parameterIndex: number - 方法中参数的索引值

示例:

function logParams(params: any) {
  return function (target: any, key: any, value: any) {
    console.log(key)
    console.log(value)
  }
}

class Greeting {
  name: string | undefined
  say (@logParams(20) age?: number) {
    return age
  }
}

var h = new Greeting()
console.log(h.say())

三、执行顺序

function logClass1(target: any) {
  console.log('类装饰器1')
}
function logClass2(target: any) {
  console.log('类装饰器2')
}

function logMethod(target: any, methodName: any, desc: any) {
  console.log('函数装饰器')
}

function logProperty (target: any, propertyName: any) {
  console.log('属性装饰器')
}

function logParam1 (target: any, key: any, index: any) {
  console.log('参数装饰器1')
}

function logParam2 (target: any, key: any, index: any) {
  console.log('参数装饰器2')
}


@logClass1
@logClass2
class Greeting {
  @logProperty
  userName: string | undefined

  @logMethod
  getData () {
    return this.userName
  }
  
  say (@logParam1 name: string, @logParam2 age: number) {
    console.log('hello world')
  }
}

输出的结果是: 属性装饰器》参数装饰器2 》参数装饰器1 》函数装饰器 》类装饰器2 》类装饰器1

结论:

  1. 如果同时存在多个装饰器,者是从后往前执行的
  2. 各类的装饰器的执行顺序是:属性装饰器 》参数装饰器》函数装饰器 》类装饰器

四、参考资源