【TS】TypeScript 装饰器

本文详细介绍了JavaScript装饰器的概念,包括类装饰器如何修改构造方法、工厂装饰器的使用、属性装饰器和方法装饰器的功能,以及存取装饰器和参数装饰器的应用。展示了如何在TypeScript中利用装饰器实现代码行为的动态修改和编译时的优化。
摘要由CSDN通过智能技术生成

文章目录

类装饰器

 【工厂装饰器】

属性装饰器

方法装饰器

存取装饰器

参数装饰器


Decorator 装饰器就是一个函数,前缀是 @ ,后跟函数表达式(或执行后返回一个函数),是一种特殊类型的声明,它能够被附加类声明、方法、属性、或者参数上也可以去修改类的行为。

装饰器会在代码加载阶段执行,而不是在运行时执行,而且只会执行一次。所以装饰器对类的行为的改变,实际上发生在编译阶段。TypeScript 装饰器能在编译阶段运行代码,其本质上就是编译时执行的函数。

使用装饰器需要在配置文件中配置 "experimentalDecorators": true 

类装饰器

类装饰器应用于类(class),但实际上是应用于类的构造方法。

类装饰器有唯一参数,就是构造方法,可以在装饰器内部,对构造方法进行各种改造。

如果类装饰器有返回值,就会替换掉原来的构造方法。

例1:下面示例中,装饰器 @ClassDecorators 返回一个新的类,替代了原来的类。

// 类装饰器
function ClassDecorators(constructor: Function) {
    console.log(constructor);
    // class User {
    //     constructor() {
    //         this.name = '小七';
    //         this.age = 20;
    //     }
    // }
    return class extends User { // 替换类
        name: string = '小九';
        age: number = 18;
    }
}

@ClassDecorators class User{
    name: string;
    age: number;
    constructor() {
        this.name = '小七';
        this.age = 20;
    }
}

let a = new User();
console.log(a.name); // 小九
console.log(User); // 替换后的类
// class extends User {
//     constructor() {
//         super(...arguments);
//         this.name = '小九';
//         this.age = 18;
//     }
// }

例2:下面示例中,装饰器 @sealed (没有返回值) 会锁定 User 这个类,使得它无法新增或删除静态成员和实例成员。

// 类装饰器
function sealed(constructor: Function) {
    Object.seal(constructor);
    Object.seal(constructor.prototype);
}

@sealed class User {
    name: string;
    age: number = 18;
    constructor(name: string) {
        this.name = name;
    }
}
 【工厂装饰器】

如果除了构造方法,类装饰器还需要其他参数,可以采取“工厂模式”,即把装饰器写在一个函数里面,该函数可以接受其他参数,执行后返回装饰器。

例2:下面示例中,函数 Factory() 的返回值才是装饰器函数,所以加载装饰器函数的时候,要先执行一次 Factory('小七') ,才能得到装饰器函数。

// 工厂装饰器
function Factory(params: string) {
    console.log(params); // 小七
    return function (target: any) { // 返回装饰器
        console.log(target); // 替换前的类
        return class extends target { // 替换类
            value = params;
        };
    }
}

@Factory('小七') class A { 
    value:string = 'abc';
}
let a = new A();
console.log(a.value); // 小七

属性装饰器

属性装饰器接收两个参数:

  1. target:装饰属性所在的类;
  2. propertyName:装饰属性的属性名。

属性装饰器不需要返回值,如果有的话,会被忽略。

下面示例中,propertyDecorators 的返回值才是装饰器函数。

// 属性装饰器
function propertyDecorators(params: any) {
    return (target: any, propertyName: string | symbol) => {
        // 装饰器传递的参数
        console.log(params);
        // 装饰器装饰属性所在的类
        console.log(target);
        // 装饰器的属性名
        console.log(propertyName);

        target[propertyName] = params;
    }
}

class User {
    // 属性装饰
    @propertyDecorators('小九')
    name: string | undefined; // 实例化时没有值为undefined
    @propertyDecorators(18)
    age: number | undefined; // 实例化时没有值为undefined
}

let b = new User();
console.log(b.name, b.age); // 小九 18

方法装饰器

方法装饰器接收三个参数:

  1. target:装饰方法所在的类;
  2. methodName:装饰方法的方法名;
  3. descriptor:装饰方法的描述对象。

如果方法装饰器有返回值,会作为该方法新的描述对象。

// 方法装饰器
function functionDecorators(params: any) {
    return (target: any, methodName: string | symbol, desc: any) => {
        // 装饰器传递的参数
        console.log(params);
        // 装饰器装饰方法所在的类
        console.log(target);
        // 装饰器的方法名
        console.log(methodName);
        // 装饰器装饰方法的描述对象
        console.log(desc);

        desc.value = function () {
            console.log(params);
        }
    }
}

class User {
    @functionDecorators('装饰器')
    hobby(): void {
        console.log("学习");
    }
}

let u = new User();
u.hobby(); // 装饰器

存取装饰器

存取器装饰器接收三个参数:

  1. target:装饰器所在的类;
  2. propertyKey:存取器的属性名;
  3. descriptor:存取器的属性描述对象。

如果存取器装饰器有返回值,会作为该属性新的描述对象。

装饰器不能同时用于同一个属性的存值器和取值器,原因是装饰器可以从属性描述对象上面,同时拿到取值器和存值器

下面示例中,存取装饰器会实现 x 在设置值的基础上加一。

// 存取装饰器
function setDecorators() {
    return (target: any, propertyKey: string, descriptor: any) => {
        // 存取器所在的类
        console.log(target);
        // 存取器的属性名
        console.log(propertyKey); // x
        // 存取器的描述对象
        console.log(descriptor);

        const originalSet = descriptor.set;
        descriptor.set = function (val: number) { // 重写set方法
            console.log(this); // 对象方法中的this指向当前对象 Point {}
            originalSet.call(this, val+1); // 设置的值加1
        }
        return descriptor; // 该属性新的描述对象
    };
}

class Point {
    #x!: number;

    @setDecorators()
    set x(val: number) {
        this.#x = val;
    }
    get x(){
        return this.#x
    }
}
let p = new Point();
p.x = 1;
console.log(p.x); // 2

参数装饰器

参数装饰器接受三个参数:

  • target:装饰器所在的类;
  • propertyKey:装饰方法的名字;
  • parameterIndex:当前参数在方法的参数序列的位置(从0开始)。

参数装饰器不需要返回值,如果有的话,会被忽略。

跟其他装饰器不同,参数装饰器主要用于输出信息,没有办法修改类的行为。

下面示例中,参数装饰器会输出参数的位置序号,后面的参数会先输出。

// 参数装饰器
function paramDecorators(target: any, propertyKey: string | symbol, paramIndex: number) {
    console.log(propertyKey, paramIndex); // position 1   // position 2
}

class Point {
    position(@paramDecorators x: number, @paramDecorators y: number) {
        console.log(x, y); // 2 4
    }
}
const p = new Point();
p.position(2, 4);

TypeScript 教程——阮一峰·著

TypeScript 中文网

TypeScript 官网

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值