typescript 五种装饰器

2019-11-26  本文已影响0人  凡凡的小web

装饰器类型

装饰器的类型有:类装饰器、访问器装饰器、属性装饰器、方法装饰器、参数装饰器,但是没有函数装饰器(function)。

1.类装饰器

应用于类构造函数,其参数是类的构造函数。
注意class并不是像Java那种强类型语言中的类,而是JavaScript构造函数的语法糖。

function addAge(args: number) {    return function (target: Function) {        target.prototype.age = args;    };} @addAge(18)class Hello {    name: string;    age: number;    constructor() {        console.log('hello');        this.name = 'yugo';    }} console.log(Hello.prototype.age);//18let hello = new Hello(); console.log(hello.age);//18

2.方法装饰器

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

function addAge(constructor: Function) {  constructor.prototype.age = 18;}​function method(target: any, propertyKey: string, descriptor: PropertyDescriptor) {   console.log(target);   console.log("prop " + propertyKey);   console.log("desc " + JSON.stringify(descriptor) + "\n\n");};​@addAgeclass Hello{  name: string;  age: number;  constructor() {    console.log('hello');    this.name = 'yugo';  }​  @method  hello(){    return 'instance method';  }​  @method  static shello(){    return 'static method';  }}

我们得到的结果是

Hello { hello: [Function] }prop hellodesc {"writable":true,"enumerable":true,"configurable":true}​​{ [Function: Hello] shello: [Function] }prop shellodesc {"writable":true,"enumerable":true,"configurable":true}

假如我们修饰的是 hello 这个实例方法,第一个参数将是原型对象,也就是 Hello.prototype。

假如是 shello 这个静态方法,则第一个参数是构造器 constructor。

第二个参数分别是属性名,第三个参数是属性修饰对象。

注意:在vscode编辑时有时会报作为表达式调用时,无法解析方法修饰器的签名。错误,此时需要在tsconfig.json中增加target配置项:

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

3. 访问器装饰器

访问器装饰器应用于访问器的属性描述符,可用于观察,修改或替换访问者的定义。 访问器装饰器不能在声明文件中使用,也不能在任何其他环境上下文中使用(例如在声明类中)。

注意: TypeScript不允许为单个成员装饰get和set访问器。相反,该成员的所有装饰器必须应用于按文档顺序指定的第一个访问器。这是因为装饰器适用于属性描述符,它结合了get和set访问器,而不是单独的每个声明。

访问器装饰器表达式会在运行时当作函数被调用,传入下列3个参数:

注意  如果代码输出目标版本小于ES5,Property Descriptor将会是undefined。

如果访问器装饰器返回一个值,它会被用作方法的属性描述符

注意  如果代码输出目标版本小于ES5返回值会被忽略。

下面是使用了访问器装饰器(@configurable)的例子,应用于Point类的成员上:

 class Point {    private _x: number;    private _y: number;    constructor(x: number, y: number) {        this._x = x;        this._y = y;    }     @configurable(false)    get x() { return this._x; }     @configurable(false)    get y() { return this._y; }}

我们可以通过如下函数声明来定义@configurable装饰器:

function configurable(value: boolean) {    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {        descriptor.configurable = value;    };}

4. 方法参数装饰器

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

const parseConf = [];class Modal {    @parseFunc    public addOne(@parse('number') num) {        console.log('num:', num);        return num + 1;    }} // 在函数调用前执行格式化操作function parseFunc(target, name, descriptor) {    const originalMethod = descriptor.value;    descriptor.value = function (...args: any[]) {        for (let index = 0; index < parseConf.length; index++) {            const type = parseConf[index];            console.log(type);            switch (type) {                case 'number':                    args[index] = Number(args[index]);                    break;                case 'string':                    args[index] = String(args[index]);                    break;                case 'boolean':                    args[index] = String(args[index]) === 'true';                    break;            }            return originalMethod.apply(this, args);        }    };    return descriptor;} // 向全局对象中添加对应的格式化信息function parse(type) {    return function (target, name, index) {        parseConf[index] = type;        console.log('parseConf[index]:', type);    };}let modal = new Modal();console.log(modal.addOne('10')); // 11

5. 属性装饰器

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

function log(target: any, propertyKey: string) {    let value = target[propertyKey];    // 用来替换的getter    const getter = function () {        console.log(`Getter for ${propertyKey} returned ${value}`);        return value;    }    // 用来替换的setter    const setter = function (newVal) {        console.log(`Set ${propertyKey} to ${newVal}`);        value = newVal;    };    // 替换属性,先删除原先的属性,再重新定义属性    if (delete this[propertyKey]) {        Object.defineProperty(target, propertyKey, {            get: getter,            set: setter,            enumerable: true,            configurable: true        });    }}class Calculator {    @log    public num: number;    square() {        return this.num * this.num;    }}let cal = new Calculator();cal.num = 2;console.log(cal.square());// Set num to 2// Getter for num returned 2// Getter for num returned 2// 4

装饰器加载顺序

image
function ClassDecorator() {    return function (target) {        console.log("I am class decorator");    }}function MethodDecorator() {    return function (target, methodName: string, descriptor: PropertyDescriptor) {        console.log("I am method decorator");    }}function Param1Decorator() {    return function (target, methodName: string, paramIndex: number) {        console.log("I am parameter1 decorator");    }}function Param2Decorator() {    return function (target, methodName: string, paramIndex: number) {        console.log("I am parameter2 decorator");    }}function PropertyDecorator() {    return function (target, propertyName: string) {        console.log("I am property decorator");    }} @ClassDecorator()class Hello {    @PropertyDecorator()    greeting: string;      @MethodDecorator()    greet( @Param1Decorator() p1: string, @Param2Decorator() p2: string) { }}

输出结果:

I am property decoratorI am parameter2 decoratorI am parameter1 decoratorI am method decoratorI am class decorator

从上述例子得出如下结论:

  1. 有多个参数装饰器时:从最后一个参数依次向前执行

  2. 方法和方法参数中参数装饰器先执行。

  3. 类装饰器总是最后执行。

  4. 方法和属性装饰器,谁在前面谁先执行。因为参数属于方法一部分,所以参数会一直紧紧挨着方法执行。

上述例子中属性和方法调换位置,输出如下结果:

I am parameter2 decoratorI am parameter1 decoratorI am method decoratorI am property decoratorI am class decorator

原文
https://blog.csdn.net/zdhsoft/article/details/90481925

上一篇 下一篇

猜你喜欢

热点阅读