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

结尾

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

相关推荐
forwardMyLife1 分钟前
element-plus 的form表单组件之el-radio(单选按钮组件)
前端·javascript·vue.js
fs哆哆14 分钟前
ExcelVBA运用Excel的【条件格式】(二)
linux·运维·服务器·前端·excel
安冬的码畜日常28 分钟前
【CSS in Depth 2精译】2.5 无单位的数值与行高
前端·css
ilisi_29 分钟前
导航栏样式,盒子模型
前端·javascript·css
吉吉安37 分钟前
grid布局下的展开/收缩过渡效果【vue/已验证可正常运行】
前端·javascript·vue.js
梦凡尘43 分钟前
Vue3 对跳转 同一路由传入不同参数的页面分别进行缓存
前端·javascript·vue.js
攒了一袋星辰43 分钟前
Webpack安装以及快速入门
前端·webpack·node.js
吃饱很舒服1 小时前
kotlin distinctBy 使用
android·java·开发语言·前端·kotlin
勤劳兔码农1 小时前
从IE到Edge:微软浏览器的演变与未来展望
前端·microsoft·edge
web守墓人1 小时前
【前端】解决element-ui两层dialog嵌套,遮罩层消失的问题。
前端·ui