TypeScript 第六章:装饰器 Decorators


环境配置

装饰器(Decorators)为我们在类的声明及成员上通过元编程语法添加标注提供了一种方式。

若要启用实验性的装饰器特性,你必须在命令行或tsconfig.json里启用experimentalDecorators编译器选项:

命令行:

tsc --target ES5 --experimentalDecorators

tsconfig.json:

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

装饰器类型

可用装饰器包括以下几种

装饰器 说明
ClassDecorator 类装饰器
MethodDecorator 方法装饰器
PropertyDecorator 属性装饰器
ParameterDecorator 参数装饰器

类装饰器

类装饰器在类声明之前被声明(紧靠着类声明)

  • 类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。

  • 首先执行 RoleDecorator 装饰器,然后执行类的构造函数

  • 装饰器会优先执行,这与装饰器与类的顺序无关

类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。


原型对象

因为可以装饰器上得到构造函数,所以可以通过原型对象来添加方法或属性,供实例对象使用

const UserDecorator: ClassDecorator = (constructor: Function) => {
    
    
    constructor.prototype.getInfo = () => {
    
    
        console.log('hello');
    }
}

@UserDecorator
class User {
    
    
    nama: string = 'hj'
    public getInfo() {
    
    
        console.log(this.nama);
    }
}

const hj = new User()
hj.getInfo() // hello

即使把装饰器定义放在类的后面也是先执行装饰器

@UserDecorator
class User {
    
    
    nama: string = 'hj'
    public getInfo() {
    
    
        console.log(this.nama);
    }
}

function UserDecorator(constructor: Function) {
    
    
    constructor.prototype.getInfo = () => {
    
    
        console.log('hello');
    }
}

const hj = new User()
hj.getInfo() // hello

语法糖

不需要把装饰器想的很复杂,下面是同样实现了装饰器的功能。只不过是我们人为调用函数,所以可以把装饰器理解为这种调用的语法糖,这样理解就简单些。

const UserDecorator: ClassDecorator = (constructor: Function) => {
    
    
    constructor.prototype.getInfo = () => {
    
    
        console.log('hello');
        
    }
}

class User {
    
    
    nama: string = 'hj'
    public getInfo() {
    
    
        console.log(this.nama);
    }
}
UserDecorator(User)

const hj = new User()
hj.getInfo() // hello

装饰器叠加

  • 装饰器可以叠加使用

  • 由上至下依次对装饰器表达式求值。

  • 求值的结果会被当作函数,由下至上依次调用。

const UserDecorator: ClassDecorator = (constructor: Function) => {
    
    
    console.log('UserDecorator 执行');
    const run = constructor.prototype.run
    constructor.prototype.run = () => {
    
    
        const num = run()
        console.log('UserDecorator求值结果:', num);
        return 3 + num
    }
}
const PersonDecorator: ClassDecorator = (constructor: Function) => {
    
    
    console.log('PersonDecorator 执行');
    constructor.prototype.getInfo = () => {
    
    
        console.log('hk');
    }
    const run = constructor.prototype.run
    constructor.prototype.run = () => {
    
    
        const num = run()
        console.log('PersonDecorator求值结果:', num);
        return 2 + num
    }
}

@UserDecorator
@PersonDecorator
class User {
    
    
    constructor() {
    
    
        console.log('User 执行');
    }
    public run () {
    
    
        return 1
    }
    public getInfo () {
    
    }
}

const hj = new User()
console.log(hj.run());
// PersonDecorator 执行
// UserDecorator 执行
// User 执行
// PersonDecorator求值结果: 1
// UserDecorator求值结果: 3
// 6
// hk

hj.getInfo() // hk

响应消息

下面是将网站中的响应消息工作,使用装饰器进行复用。

const MessageDecorator: ClassDecorator = (constructor: Function) => {
    
    
    constructor.prototype.message = (msg: string) => {
    
    
        console.log(msg);
    }
}

@MessageDecorator
class LoginController {
    
    
    public login () {
    
    
        console.log('登录逻辑');
        this.message('登陆成功')
    }
}

const controller = new LoginController()
controller.login()

装饰器工厂

如果我们要定制一个修饰器如何应用到一个声明上,我们得写一个装饰器工厂函数。 装饰器工厂就是一个简单的函数,它返回一个表达式,以供装饰器在运行时调用。

  • 我们可以通过下面的方式来写一个装饰器工厂函数:
function color(value: string) {
    
     // 这是一个装饰器工厂
    return function (target) {
    
     //  这是装饰器
        // do something with "target" and "value"...
    }
}

  • 下例根据 MusicDecorator 工厂函数传递的不同参数,返回不同装饰器函数。
const MusicDecortaor = (type: string): ClassDecorator => {
    
    
    switch(type) {
    
    
        case 'player-1': {
    
    
            return (constructor: Function) => {
    
    
                constructor.prototype.playMusic = () => console.log('播放【战车恰恰】');
            }
        } 
        default: {
    
    
            return (constructor: Function) => {
    
    
                constructor.prototype.playMusic = () => console.log('播放【勇士之歌】');
            } 
        }
    }
}

@MusicDecortaor('player-1')
class PlayerOne {
    
    }

@MusicDecortaor('player-2')
class PlayerTwo {
    
    }

const player1 = new PlayerOne() as any
const player2 = new PlayerTwo() as any
player1.playMusic() // 播放【战车恰恰
player2.playMusic() // 播放【勇士之歌】

方法装饰器

装饰器也可以修改方法,首先介绍装饰器函数参数说明

  • 参数一,普通方法是构造函数的原型对象 Prototype,静态方法是构造函数
  • 参数二,方法名称
  • 参数三,属性描述

基本使用

  • 下面使用 ShowDecorator 装饰来修改 show 方法的实现
const ShowDecorator: MethodDecorator = (
  target: object,
  porpertyKey: string | symbol,
  descriptor: PropertyDescriptor
): void => {
    
    
    descriptor.value = () => {
    
    
      console.log("画戟");
    };
};

class User {
    
    
  @ShowDecorator
  public show() {
    
    
    console.log("hj");
  }
}
const hj = new User();
hj.show(); // 画戟

  • 下面是修改方法的属性描述 writable 为 false,这时将不允许修改方法。
const ShowDecorator: MethodDecorator = (
  target: object,
  porpertyKey: string | symbol,
  descriptor: PropertyDescriptor
): void => {
    
    
  descriptor.writable = false;
};

class User {
    
    
  @ShowDecorator
  public show() {
    
    
    console.log("hj");
  }
}
const hj = new User();
hj.show = () => {
    
    
  console.log("111111");
};
hj.show(); // hj

静态方法

静态方法使用装饰器与原型方法相似,在处理静态方法时装饰器的第一个参数是构造函数。

const ShowDecorator: MethodDecorator = (
  target: object,
  porpertyKey: string | symbol,
  descriptor: PropertyDescriptor
): void => {
    
    
    descriptor.value = () => {
    
    
      console.log("画戟");
    };
};

class User {
    
    
  @ShowDecorator
  static show() {
    
    
    console.log("hj");
  }
}
User.show(); // 画戟

自定义捕获错误

const ShowDecorator: MethodDecorator = (
  target: object,
  porpertyKey: string | symbol,
  descriptor: PropertyDescriptor
): void => {
    
    
    const method = descriptor.value
    descriptor.value = () => {
    
    
      try {
    
    
        method()
      } catch (err) {
    
    
        console.error(err);
      }
    };
};

class User {
    
    
  @ShowDecorator
  public show() {
    
    
    throw new Error('错误')
  }
}
const hj = new User();
hj.show(); // Error: 错误

属性装饰器

首先介绍装饰器函数参数说明

  • 参数一 普通方法是构造函数的原型对象 Prototype,静态方法是构造函数
  • 参数二 属性名称

基本使用

  • 下面是属性装饰器的定义方式
const PropsDecorator: PropertyDecorator = (target: object, porpertyKey: string | symbol): void => {
    
    
    console.log(target, porpertyKey);
}

class User {
    
    
    @PropsDecorator
    public name: string = 'hj'
    public show() {
    
    
        console.log(33);
    }
}
const hj = new User() // {} name

访问器

下面是通过setter、getter访问器操作属性

const PropDecorator: PropertyDecorator = (target: object, propertyKey: string | symbol) => {
    
    
    let value: string;
    const getter = () => {
    
    
        return value
    }
    const setter = (v: string) => {
    
    
        value = v.toLowerCase()
    }

    Object.defineProperty(target, propertyKey, {
    
    
        set: setter,
        get: getter
    })
}
class Hj {
    
    
    @PropDecorator
    public name: string | undefined
    show() {
    
    
        console.log(33);
    }
}

const instance = new Hj();
instance.name = 'Hj'
console.log(instance.name); // hj

参数装饰器

可以对方法的参数设置装饰器,参数装饰器的返回值被忽略

  • 参数一,普通方法是构造函数的原型对象 Prototype,静态方法是构造函数
  • 参数二,方法名称
  • 参数三,参数所在索引位置

基本使用

下面是定义参数装饰器

const ParamsDecorator: ParameterDecorator = (target: object, propertyKey: string | symbol, parameterIndex: number) => {
    
    
    console.log(target, propertyKey, parameterIndex); // {} hj 1
}

class User {
    
    
    public hj (name: string, @ParamsDecorator age: number): void {
    
    
        console.log(name);
        console.log(age);
    }
}
const hj = new User()

元数据

元数据指对数据的描述,首先需要安装扩展包

npm i reflect-metadata
// 引入支持元数据的扩展名
import 'reflect-metadata'

const hj = {
    
     name: 'hj', age: 18 }
// 在对象 hj 的属性 name 上定义元数据 (元数据指对数据的描述)
Reflect.defineMetadata('hhh', 'hk', hj, 'name')
let value = Reflect.getMetadata('hhh', hj, 'name')
console.log(value); // hk
console.log(hj); // { name: 'hj', age: 18 }

猜你喜欢

转载自blog.csdn.net/qq_41887214/article/details/125652244