前言🥇🥇
大家好,我们继续学习TS,本次我们主要会讲解装饰器,这个对于初学者会比较的难以理解,但是个人认为这部分是很重要的,尤其是如果大家用过nestjs的话,这个部分和这个技术相关性比较大,大家可以好好学学
装饰器的学习🍰🍰
装饰器是什么?
装饰器(Decorator)是 TypeScript 中的一种高级功能,用于修改或扩展类、方法、属性或参数的行为,而不需要修改它们的原始代码。 装饰器通常用于元编程,可以让你在不改变源代码的情况下,添加额外的功能或元数据。
首先装饰器分为类装饰器,方法装饰器,属性装饰器,参数装饰器这几种。我们逐一来学习:
类装饰器
其实装饰器的本质就是一个函数,只不过使用的位置不一样,才导致了用途和名称不同,下面我们就来介绍一下类装饰器:
const Logger:ClassDecorator=(target)=> { console.log("Logging class:", target); } @Logger class MyClass { // Class definition }
这个是最基础的写法,这个有什么用处呢?
其实我们在类的前面加上装饰器之后,这个装饰器中会接收一个参数,这个参数就是这个类的构造函数。我们可以通过这个构造函数来向这个类中增添新的属性和方法。
当然我们还可以向装饰器中传递参数,这个该如何实现呢?
这里面我们用到了函数的柯里化(你可以理解为闭包)
const Base = (name: string) => { const fn: ClassDecorator = (target) => {}; return fn; }; @Base("sdasd") class Http { }
我们这时就可以在name中接收到传递过来的值,然后函数内部中的target接收的依然是Http这个类的构造函数。
方法装饰器
紧接着我们来讲解一下方法装饰器,其实这个是类似的。
介绍:方法装饰器用于修饰类的方法,它们接受三个参数:目标对象、属性名和属性描述符,并可以用来修改或拦截方法的行为。 方法装饰器通常用于添加日志、权限控制等功能。
代码演示:
class MyClass { @LogMethod myMethod() { // Method definition } } const LogMethod = (target: Object, Key: string, descriptor: PropertyDescriptor)=> { console.log(target, key, descriptor); }
在这个例子中Key是方法的名称,比如这里面我们装饰器修饰的方法是myMethod,那么这里Key就会打印"myMethod"这个方法名。然后target这个表示的是被装饰方法所属的类的原型对象(prototype) ,在上面这个例子中就是MyClass这个类的原型对象,我们可以通过这个向原型对象上面增添属性和方法。
而最后这个参数是一个用于描述对象属性特性的对象,这个对象中包含四个值,分别为value,writable,enumerable,configurable,在这里我们只需要知道value这个值是被修饰的方法。我们可以通过这个访问到被修饰的方法,比如向这个方法传递参数等等。
在学习上面的这两个装饰器之后我们可以给大家举一个例子明白装饰器到底是怎样使用的:
@Base("sdasd") class Http { @GET("http://localhost:3000/api/user") getList(data: any) { console.log(data.result.list); } } const GET = (url: string) => { const fn: MethodDecorator = (target, key, descriptor: PropertyDescriptor) => { axios.get(url).then((res) => { descriptor.value(res); }); console.log(target, key, descriptor); }; return fn; };
这里我们相当于是在@GET这个装饰器中实现了对于接口的请求,并且我们将获取到的返回值直接传递给了getList方法,如果读者在看这篇文章的时候学习过nestjs/java的话,相信对这个并不陌生,我们将请求接口的具体操作封装子了装饰器中,这样我们的代码看上去就可以更加的简洁了,而且复用性也很高。
参数装饰器
首先我们还是先介绍一下参数装饰器
参数装饰器用于修饰类的方法参数。它们接受三个参数:目标对象、方法名和参数索引,并可以用来修改方法参数的行为或添加元数据。 参数装饰器通常用于依赖注入、参数验证等场景。
class MyClass { myMethod(@ParameterDecorator param: string) { // Method definition } } function ParameterDecorator(target: Object, Key: string, index: number) { // Modify or validate the parameter here }
其实这里接收的参数和方法装饰器类似,第一个参数还是这个类的原型对象,这个key还是被修饰的参数所属的方法的方法名,这个index则是被修饰参数的索引,
表明被修饰参数所处的位置,比如在这个例子中这个index就为0
介绍完成之后我们会结合之前的例子:
不知道大家发现了没有,我们之前在getList中接收参数的时候还需要手动的data.result.list,这样每次都需要通过这种方式来获取返回值,是不是稍微有点麻烦?有没有更好的方式呢?我们可以使用参数装饰器解决。
我们可以在getList中data参数的前面加上一个装饰器,这里我们定义一个比如为@Result,下面我们通过代码实现一下
const Result = (str: string) => { const fn: ParameterDecorator = (target, key, index) => { console.log(target, key, index); Reflect.defineMetadata("key", str, target); }; return fn; }; const GET = (url: string) => { const fn: MethodDecorator = (target, key, descriptor: PropertyDescriptor) => { const Key = Reflect.getMetadata("key", target); axios.get(url).then((res) => { descriptor.value(Key ? res.data[key] : res.data); }); }; return fn; }; class Http { @GET("http://localhost:3000/api/user") getList(@Result("result") data: any) { console.log(data); } }
注意这里面我们使用了Reflect,这里面我们需要下载第三方的包
npm i reflect-metadata
这里面我们使用了反射,将传递过来的值存了起来,使他后面可以通过在方法中读取而获得。注意这里调用Reflect.defineMetadata所传递的值
- key:这是一个字符串或符号,用于唯一标识元数据。通常,不同的元数据应该有不同的键,以确保它们不会互相冲突。
- value:这是你想要存储的任意值,可以是对象、字符串、数字、布尔值,甚至是函数等。元数据的值可以根据需要包含各种信息,以供后续使用。
- target:这是要附加元数据的目标对象。通常,目标对象可以是类的原型、类的构造函数、类的方法、属性等。元数据将与目标对象相关联,以便在运行时可以检索它。
我们在这里使用Reflect将值先保存了下来,然后在GET方法中读取,判断是否有这个值,如果有这个值就对返回的值进行加工之后在进行返回。这样我们就可以直接在返回的数据中通过Reflect.getMetadata读取到result这一层了。而且这个也是可以复用的,也是比较的方便。因此学好装饰器还是很重要的。
属性装饰器
最后一个就是属性装饰器了,这个装饰器用到的不是很多,这里我们简单的了解一下:
const Name: PropertyDecorator = (target, key) => { console.log(target, key); }; class Http { @Name name: string; constructor() { this.name = "sda"; } }
同样,这里面的target还是所在类的原型对象,这个key就是所修饰的属性的属性名。
到这里我们就将所有的装饰器全部都介绍完毕了,不知道大家有没有掌握,其实如果对于学过nestjs或者java的同学这部分应该会好理解一些,使用的场景就会多一些。
小结⛹️♀️⛹️♀️
本次我们主要学些了TS中的装饰器,这部分知识不是很好理解,大家要多看才能理解,希望大家都能掌握