# 类的装饰器

# 概念

首先装饰器本身是一个函数,修饰类的装饰器,接收的参数是一个构造函数;

需要注意的是我们使用装饰器是不能直接使用的,,需要打开 tsconfig.json 的一个配置 "experimentalDecorators": true 与 "emitDecoratorMetadata": true

function testDecorator(constructor: any) {
  constructor.prototype.getName = () => {
    console.log(111)
  }
  console.log(111)
}
// 装饰器通过 @ 符号来使用
// 装饰器执行的顺序是从下到上,从右到左的顺序
@testDecorator
class Test {}

注意,装饰器通过 @ 符号来使用,多个装饰器执行的顺序是从下到上,从右到左的顺序;

装饰器函数也可以在里面做判断:

// 类的装饰器
// 装饰器本身是一个函数
function testDecorator(flag: boolean) {
  if (flag) {
    // 修饰类的装饰器,接收的参数是一个构造函数
    return function (constructor: any) {
      constructor.prototype.getName = () => {
        console.log(111)
      }
      console.log(111)
    }
  }
  return function (constructor: any) {}
}
// 装饰器通过 @ 符号来使用
// 装饰器执行的顺序是从下到上,从右到左的顺序
@testDecorator(true)
class Test {}
const descTesart = new Test()(descTesart as any).getName()

通过装饰器扩展修改属性:

function testDecorator<T extends new (...args: any[]) => any> (constructor: T) {
    return class extends constructor {
        name = 'cj'
    }
}

@testDecorator
class Test {
    name: string
    constructor(name: string) {
        this.name = name
    }
}
const descTesart = new Test('jie')
console.log(descTesart.name)

类装饰方法。 上面的代码在调用 getName 方法的时候,会报错,需要使用 (descTesart as any).getName() 这种方式来调用,我们可以修改代码,来解决这个问题:

function testDecorator() {
  return function <T extends new (...args: any[]) => any>(constructor: T) {
    return class extends constructor {
      getName() {
        return this.name
      }
    }
  }
}
const Test = testDecorator()(
  class {
    name: string
    constructor(name: string) {
      this.name = name
    }
  }
)
const descTesart = new Test('jie')
console.log(descTesart.getName())

# 类中方法的装饰器

// 普通方法,target 对应的是类的 prototype
// 静态方法 target 对应的是类的构造函数
// key 装饰的方法的名字
// descriptor 类似属性描述符
function getNameDecorator(
  target: any,
  key: string,
  descriptor: PropertyDescriptor
) {
  // 方法不可以被重写
  descriptor.writable = false
  // 修改方法
  descriptor.value = function () {
    return 'decorator'
  }
}
class Test {
  name: string
  constructor(name: string) {
    this.name = name
  }
  @getNameDecorator
  getName() {
    return this.name
  }
}
const test = new Test('jie')
console.log(test.getName())

# 类中访问器的装饰器

// 同方法的访问器参数
function visitDecorator(
  target: any,
  key: string,
  descriptor: PropertyDescriptor
) {
  // 不可修改
  // descriptor.writable = false
  descriptor.value = '3333'
}
class Test {
  private _name: string
  constructor(name: string) {
    this._name = name
  }
  get name() {
    return this._name
  }
  @visitDecorator
  set name(name: string) {
    this._name = name
  }
}
const test = new Test('jie')
test.name = '123'
console.log(test.name)

# 属性的装饰器

属性的装饰器与类的跟方法的装饰器是不一样的,接收的参数是没有修饰符对象的。

// target 原型
// key 属性名字
function nameDecorator(target: any, key: string) {
  console.log(target, key) // Test{} name
}

class Test {
  @nameDecorator
  name: 'jie'
}
const test = new Test()
console.log(test.name)

如果我们需要设置属性不能修改,需要我们进行自定义装饰符对象:

function nameDecorator(target: any, key: string): any {
  const descriptor: PropertyDescriptor = {
    writable: false,
  }
  return descriptor
}

class Test {
  @nameDecorator
  name: 'jie'
}
const test = new Test()
console.log(test.name)

如果我们需要修改属性值:

// 修改的并不是实例上的 name,而是原型上的 name
function nameDecorator(target: any, key: string): any {
  // 这样直接修改数值是不行的--这里修改的是 prototype 上面的 name
  target[key] = 'giser'
}

class Test {
  @nameDecorator
  name: 'jie'
}
const test = new Test()
// test.name = 'giser' // 在这里进行修改
console.log(test.name)

# 参数装饰器

也就是对类中方法的参数进行装饰;他跟前面的装饰器接收的参数都是不一样的:

// 原型、方法名、参数所在的位置
function paramDecorator(target: any, key: string, paramIndex: number): any {
  console.log(target, key, paramIndex)
}

class Test {
  getInfo(@paramDecorator name: string, age: number) {
    console.log(name, age)
  }
}
const test = new Test()
test.getInfo('jie', 18)

# 装饰器实际使用的小例子

比如我们有如下代码,我们访问一个 any 类型的对象,在类中访问他的属性,会报错,我们可以添加 try catch 来防止报错, 但是如果这样的话,需要添加很多 try catch;

const userInfo: any = undefined

class Test {
  getName() {
    try {
      return userInfo.name
    } catch (e) {
      console.log('userInfo.name 不存在')
    }
  }
  getAge() {
    try {
      return userInfo.age
    } catch (e) {
      console.log('userInfo.age 不存在')
    }
  }
}
const test = new Test()
test.getName()

我们可以通过方法装饰器来解决这个问题:

const userInfo: any = undefined
function catchError(msg: string) {
  return function (target: any, key: string, descriptor: PropertyDescriptor) {
    const fn = descriptor.value
    descriptor.value = function () {
      try {
        fn()
      } catch (e) {
        console.log(`${msg} 不存在`)
      }
    }
  }
}
class Test {
  @catchError('userInfo.name')
  getName() {
    return userInfo.name
  }
  @catchError('userInfo.age')
  getAge() {
    return userInfo.age
  }
}
const test = new Test()
test.getName()

评 论:

更新: 11/21/2020, 7:00:56 PM