摸鱼偷学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);

结尾

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

相关推荐
一个处女座的程序猿O(∩_∩)O2 小时前
小型 Vue 项目,该不该用 Pinia 、Vuex呢?
前端·javascript·vue.js
hackeroink5 小时前
【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具
前端·xss
迷雾漫步者6 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart
向前看-7 小时前
验证码机制
前端·后端
燃先生._.8 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
高山我梦口香糖9 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
m0_748235249 小时前
前端实现获取后端返回的文件流并下载
前端·状态模式
m0_748240259 小时前
前端如何检测用户登录状态是否过期
前端
black^sugar10 小时前
纯前端实现更新检测
开发语言·前端·javascript