修饰器:修饰器是一种特殊类型的声明,它只能够被附加到类的声明、方法、属性或参数上,可以修改类的行为。而不能用于函数(因为存在函数提升)
常见的修饰器有:类修饰器、属性修饰器、方法修饰器、参数修饰器
修饰器写法:普通修饰器(无法传参)、修饰器工厂(可传参)

core-decorators.jsvue-property-decorator 是第三方模块,提供了几个常见的修饰器,通过它可以更好的理解修饰器

类修饰器

  • 类修饰器在类声明之前被声明(紧靠着类声明)
  • 类修饰器应用于类构造函数,可以用来监视,修改或替换类定义
function logClass (params: any) {
  console.log(params) // [Function: HttpClient]
  params.prototype.fName = '动态扩展的属性'
  params.prototype.run = () => {
    console.log('动态扩展的方法')
  }
}

@logClass
class HttpClient {}

let fun: any = new HttpClient()
console.log(fun.fName) // 动态扩展的属性
fun.run() // 动态扩展的方法

修饰器工厂(闭包传参)

function logClass (params: string) {
  return function (target: any) {
    console.log(target) // [Function: HttpClient]
    console.log(params) // hello
    target.prototype.fName = '动态扩展的属性'
    target.prototype.run = () => {
      console.log('动态扩展的方法')
    }
  }
}

@logClass('hello')
class HttpClient {}

let fun: any = new HttpClient()
console.log(fun.fName) // 动态扩展的属性
fun.run() // 动态扩展的方法

重载构造函数的例子

function logClass (target: any) {
  return class extends target {
    apiUrl: any = '重载属性'

    getData () {
      console.log('重载方法', this.apiUrl)
    }
  }
}

@logClass
class HttpClient {
  apiUrl: string | undefined

  constructor () {
    this.apiUrl = '构造函数的 apiUrl'
  }

  getData () {
    console.log(this.apiUrl)
  }
}

let http = new HttpClient()
http.getData() // 重载方法 重载属性

属性修饰器

属性装饰器表达式会在运行时当作函数被调用,传入下列 2 个参数

  1. 对于静态成员来说时类的构造函数,对于实例成员是类的原型对象
  2. 成员名字
// 属性修饰器 logProperty
function logProperty (params: any) {
  return function (target: any, name: any) {
    console.log(target, name) // HttpClient { getData: [Function] } 'url'
    target[name] = params
  }
}

class HttpClient {
  @logProperty('属性修饰器的参数')
  url: any | undefined

  constructor () {}

  getData () {
    console.log(this.url) // 属性修饰器的参数
  }
}

let fun = new HttpClient()
fun.getData()

方法修饰器

它会被应用到方法的属性描述符上,可以用来监听、修改或者替换方法定义
方法修饰器会在运行时传入下列三个参数:

  1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
  2. 成员的名字
  3. 成员的属性描述符
// 方法修饰器
function logFunction (params: any) {
  return function (target: any, methodName: any, desc: any) {
    console.log(target, methodName, desc)
    /*
    HttpClient { getData: [Function] }
    'getData'
    {
      value: [Function], // 值
      writable: true, // 可读
      enumerable: true, // 可枚举
      configurable: true // 可设置
      }
    */

    target.fName = '动态扩展的属性'
    target.run = () => {
      console.log('动态扩展的方法')
    }

    // 将接收到的参数改为 string 类型
    let oMethod = desc.value
    desc.value = function (...args: any) {
      args = args.map((v: any) => {
        return String(v)
      })
      return oMethod.apply(this, args)
    }
  }
}

class HttpClient {
  @logFunction('方法修饰器的参数')
  getData (...args: any[]) {
    console.log(args)
  }
}

let fun: any = new HttpClient()
console.log(fun.fName) //动态扩展的属性
fun.run() // 动态扩展的方法

fun.getData(123, '234', () => {}) // [ '123', '234', 'function () { }' ]

参数修饰器

参数修饰器表达式会在运行是当作函数被调用,可以使用参数修饰器为类的原型增加一些元素数据,传入下列三个参数

  1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
  2. 参数的名字
  3. 参数在函数参数列表中的索引
function logParams (params: any) {
  return function (target: any, methodName: any, paramsIndex: any) {
    console.log(target, methodName, paramsIndex) // HttpClient { getData: [Function] } 'getData' 0
    target.param = params
  }
}

class HttpClient {
  getData (@logParams('参数修饰符的参数') id: any) {
    console.log(id)
  }
}

let fun: any = new HttpClient()
fun.getData(123)
console.log(fun.param) // 参数修饰符的参数

类修饰器、属性修饰器、方法修饰器、参数修饰器执行顺序

function log (params: any) {
  return function () {
    console.log(params)
  }
}

@log('类修饰器')
class HttpClient {
  @log('属性修饰器')
  id: any

  @log('方法修饰器')
  getData (@log('参数修饰器') id: any) {
    this.id = id
  }
}

new HttpClient()
// 执行顺序:属性修饰器>参数修饰器>方法修饰器>类修饰器