# 装饰器

装饰器是一种针对class类的特殊类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为。通俗来讲装饰器就是一个方法,可以注入到类、方法、属性参数上来扩展类、属性、方法、参数的功能,在你上面声明一个符号,我通过这个符号来控制你。就像是封装一个函数,只需要知道结果,具体要做什么不用问,通过装饰器来完成,完成后把结果给到被装饰的就行。

常见的装饰器有: 类装饰器、方法装饰器、参数装饰器、属性装饰器

所有装饰器执行顺序: 属性》方法》方法参数》类,如果多个同类型装饰器 会先执行后面的

用在谁上面就装饰谁,放在类上面就针对整个类装饰,放在类的属性上面,就针对这个属性修饰,放在方法上面就针对这个方法,每种修饰都有不同的写法。

要使用装饰器,需要开启配置 experimentalDecorators: trueemitDecoratorMetadata: true,定义装饰首字母一般大写。

# 类装饰器

放在类前面,来监视、修改或替换类定义

普通类装饰器(不能传参)

// 默认接收一个参数target,target为当前类的构造函数
// 通过target可以更改原型上的属性和方法
const Base:ClassDecorator = (target) => {
  console.log(target); // ƒ HttpClient() {}  target就是当前类 HttpClient
  target.prototype.url = '动态扩展的属性'
  target.prototype.run = function() {
    console.log('动态添加方法')
  }
}


@Base
class HttpClient {
  constructor() {}
  getData() {}
}

let http = new HttpClient()

console.log(http.url) // 动态扩展的属性
http.run()

装饰器工厂(可传参)

// 通过return一个函数来传入参数
const Base = (name: string) => {
  const fn: ClassDecorator = (target) => {
    target.prototype.name = name
    target.prototype.run = function() {
      console.log('动态添加方法')
    }
  }
  return fn
}


@Base('zhangsan')
class HttpClient {
  constructor() {}
  getData() {

  }
}

let http = new HttpClient();

console.log(http.name)
http.run();

重载类里面的属性和方法

function Base(target:any) {
  return class extends target {
    url:string = '我是修改后的url';
    getData():void {
      this.url = this.url+'xxxxxxxxxxx'
      console.log(this.url)
    }
  }
}

@Base
class HttpClient {
  url:string | undefined
  constructor() {
    this.url = '我是构造函数中的url'
  }
  getData() {
    console.log(this.url)
  }
}

let http = new HttpClient3();

console.log(http.url)
http.getData();

# 属性装饰器

属性装饰器表达式会在运行时当做函数被调用,传入两个参数:

  • 1、对于静态成员来说是类的构造函数,对于实力成员是类的原型对象
  • 2、属性的名字
// 属性装饰器
const Property = (newUrl: string) => {
  const fn: PropertyDecorator = (target:any, attr:any) => {
    console.log(target)  // {getData: ƒ, constructor: ƒ}  类的原型对象
    console.log(attr)    // url  类的属性
    // 扩展或者修改属性
    target[attr] = newUrl
    target.test = 'hahahahahahah'
  }
  return fn
}

class HttpClient {
  @Property('http://www.tourboxtech.com')
  url: string | undefined
  constructor() {
  }
  getData() {
    console.log(this.url)
  }
}

let http = new HttpClient();
console.log(http.test);  // hahahahahahah  装饰器扩展的属性
http.getData(); // http://www.tourboxtech.com  装饰器修改的属性

# 方法装饰器

它会被应用到方法的 属性描述符上,可以用来监视,修改或者替换方法定义,传入三个参数:

  • 1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
  • 2、成员的名字、
  • 3、成员的属性描述
// 方法装饰器
const Method = (newUrl: string) => {
  const fn:MethodDecorator = (target: any, methodName: any, descriptor: PropertyDescriptor) => {
    console.log(target)      // {getData: ƒ, constructor: ƒ}  类的原型对象
    console.log(methodName)  // getData  方法名
    console.log(descriptor)        //  描述
    console.log(descriptor.value)  //  类的方法

    // 扩展或者修改属性和方法
    target.url = newUrl
    target.test = 'http://www.xxxx.com'
    target.run = function() {
      console.log(this.url)
    }

    // 修改类的方法,重新赋值一个方法就行了
    descriptor.value = () => {
      console.log(target.test)
    }
  }
  return fn
}

class HttpClient {
  url:string | undefined
  constructor() {
  }
  @Method('hello')
  getData() {
    console.log(this.url)
  }
}

let http = new HttpClient()
http.run();     // hello  装饰器扩展的方法
http.getData(); // http://www.xxxx.com   装饰器修改原方法

# 方法参数装饰器

它会被应用到方法的参数上,可以为原型增加一些元素数据,传入三个参数:

  • 1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
  • 2、方法的名字
  • 3、参数在函数参数列表的索引
// 方法参数装饰器
const Params = (params: any) => {
  console.log(params) // 装饰器传入的参数

  const fn: ParameterDecorator = (target: any, propertyKey: any, parameterIndex: number) => {
    console.log(target)      // {getData: ƒ, constructor: ƒ}  类的原型对象
    console.log(propertyKey)  // getData  方法名
    console.log(parameterIndex)  //  0  索引
    target.test = params
  }
  return fn
}

class HttpClient {
  url:string | undefined
  constructor() {
  }
  getData(@Params('我是参数') uuid:any) {
    console.log(uuid)
  }
}
let http = new HttpClient();
http.getData(12233);
http.test;