摸鱼偷学TS:装饰器

浅聊一下

装饰器(decorator)最早在 Python 中被引入,它的主要作用是给一个已有的方法或类扩展一些新的行为,而不是去直接修改它本身.

ES2015 进入 Class 之后,当我们需要在多个不同的类之间共享或者扩展一些方法或行为的时候,代码会变得错综复杂,极其不优雅,这也就是装饰器被提出的一个很重要的原因

装饰器

Typescript 中我们需要在 tsconfig.json 里面开启支持选项

ts 复制代码
// tsconfig.json
"experimentalDecorators": true

类装饰

想象一个场景,有一个Person类和一个addAge函数,addAge函数可以给Person添加一个年龄的属性,我们该如何来实现?

ts 复制代码
function addAge(constructor: Function) {
    constructor.prototype.age = 18;
}
class Person {
    name: string;
    age!: number;
    constructor() {
        this.name = 'juejin';
    }
}
addAge(Person);
let person = new Person();
console.log(person.age);//18
  • 我们首先定义了 addAge 函数,它接受一个构造函数作为参数,并给这个构造函数的原型添加一个名为 age 的属性,赋值为 18
  • 然后我们定义了 Person 类,其中包含一个 name 属性和一个构造函数,用于初始化 name。此时,我们不再需要在类定义中声明 age 属性,因为它将通过原型链被添加。
  • 接下来,我们调用 addAge(Person),将 Person 构造函数传递给 addAge 函数,这样就会在 Person 的原型上添加 age 属性。

注意这里我们并没有使用装饰器,那么使用装饰器又该如何来写

ts 复制代码
function addAge(constructor: Function) {
    constructor.prototype.age = 18;
}
@addAge
class Person {
    name: string;
    age!: number;
    constructor() {
        this.name = 'juejin';
    }
}
let person = new Person();
console.log(person.age);//18

Person 类定义之前,使用了 @addAge 装饰器。这相当于在类定义完成后立即执行 addAge(Person),从而向 Person 的原型上添加了 age 属性。

总的来说,装饰器提供了一种优雅的方式来修改或增强类的行为,而无需改动类的原始代码。

属性、方法装饰器

一个类的属性和方法也可以被装饰

ts 复制代码
function method(target: any, key: string,descriptor: PropertyDescriptor) {
    console.log(target);
    console.log('prop ' + key);
    console.log('desc' + JSON.stringify(descriptor) + '\n\n');
    descriptor.writable = false;
}
class Person {
    name: string;
    constructor() {
        this.name = 'juejin';
    }
    @method
    say(){
        return 'instance method'
    }
    @method
    static run() {
        return 'static method'
    }
}
let person = new Person();
person.say = function() {
    return 'edit'
}
console.log(person.say());
  1. 首先定义了一个装饰器函数,接受三个参数
ts 复制代码
function method(target: any, key: string, descriptor: PropertyDescriptor) {
    console.log(target); // 打印目标对象(对于实例方法,是类的原型;对于静态方法,是类本身)
    console.log('prop ' + key); // 打印被装饰的方法名
    console.log('desc' + descriptor + '\n\n'); // 打印方法的描述符
    descriptor.writable = false; // 设置描述符的writable属性为false,尝试修改方法会失败
}
  • target: 对于实例方法,它是类的原型;对于静态方法,它是类本身。
  • key: 被装饰的方法的名称。
  • descriptor: 方法的属性描述符,它是一个对象,包含了该属性的特性(如 value, writable, configurable, enumerable 等)。
  1. Person类

在Person类中的say方法和run方法前加上装饰器函数

  1. 调用

想修改Person类的say方法,并且调用say方法查看是否改动

先来看看结果

首先可以看到我们并没有调用run方法,可是run方法被调用了

这是因为在类定义的时候,就会调用装饰器函数,即使我们没有实例化类对象。 而报错是因为我们无法修改一个只读属性

在属性/方法的装饰器定义过程中,与 class 的装饰器不同,我们的 method 函数中的参数变为了三个 target propertyKey descriptor.这三个参数正是源于Object.defineProperty

上面的方法装饰器代码相当于下面:

ts 复制代码
let descriptor = {
    value: function() { return 'instance method'},
    enumerable: false,
    configurable: true,
    writable: true
};

descriptor = readonly(Cat.prototype, "say", descriptor) || descriptor;

Object.defineProperty(Cat.prototype, "say", descriptor);

结尾

写到这里,该去吃饭了...

相关推荐
Myli_ing1 小时前
HTML的自动定义倒计时,这个配色存一下
前端·javascript·html
dr李四维1 小时前
iOS构建版本以及Hbuilder打iOS的ipa包全流程
前端·笔记·ios·产品运营·产品经理·xcode
雯0609~1 小时前
网页F12:缓存的使用(设值、取值、删除)
前端·缓存
℘团子এ2 小时前
vue3中如何上传文件到腾讯云的桶(cosbrowser)
前端·javascript·腾讯云
学习前端的小z2 小时前
【前端】深入理解 JavaScript 逻辑运算符的优先级与短路求值机制
开发语言·前端·javascript
前端百草阁2 小时前
【TS简单上手,快速入门教程】————适合零基础
javascript·typescript
彭世瑜2 小时前
ts: TypeScript跳过检查/忽略类型检查
前端·javascript·typescript
FØund4042 小时前
antd form.setFieldsValue问题总结
前端·react.js·typescript·html
Backstroke fish2 小时前
Token刷新机制
前端·javascript·vue.js·typescript·vue
小五Five2 小时前
TypeScript项目中Axios的封装
开发语言·前端·javascript