最近在学习使用 nest 框架开发公司管理系统,nest 大量使用了装饰器这个东西,所以在此记录并分享学习到的东西。
一、 什么是装饰器
装饰器是一种特殊的类型声明,能够附加到类型,方法,属性或者参数上,可以修改类的行为。装饰器的好处:
- 好的装饰器就是代码注释;
- 帮助代码解耦;
- 提高代码的复用性;
- 代码更加简洁, 更便于阅读;
二、装饰器分类
装饰器 是一个函数,它可以通过 @funName 在类、方法、访问符、属性、参数上,对它们进行包装,然后返回一个包装后的目标对象(类、方法 、访问符、属性、参数)。
1. 类装饰器
类装饰器应用于类的构造函数,可以用来监视,修改,和替换类定义
function logClass(param:any){
console.log(param);
#修改类的属性和方法
param.prototype.aaa = "xxx";
param.prototype.getMessage = () => {
console.log('getMessage');
}
}
@logClass
class A {
constructor() {}
getData() {
console.log('getData');
}
}
# 可以看到动态扩展属性方法已经成功了
const a = new A();
console.log((a as any).aaaa); // xxx
console.log((a as any).getMessage()); // getMessage
2. 装饰器工厂
只要在普通装饰器的基础上return一个函数即可变成装饰器工厂
function logClass(param:any){
console.log(param);
# param 变成了传进来的值,而 return 的函数的参数 target 就变成了类 A,修改类的属性和方法
return function(target) {
target.prototype.aaa = "xxx";
target.prototype.getMessage = () => {
console.log('getMessage');
}
}
}
@logClass("装饰器工厂")
class A {
constructor() {}
getData() {
console.log('getData');
}
}
# 可以看到动态扩展属性方法已经成功了
const a = new A(); // 装饰器工厂
console.log((a as any).aaaa); // xxx
console.log((a as any).getMessage()); // getMessage
总结,类装饰器一般是一个函数,分为装饰器工厂和普通装饰器,主要是原理就是类的构造函数会通过参数传入该装饰器(函数),在该函数上对类做一系列的扩展,加强该类
3. 属性装饰器
属性装饰器接受两个参数,第一个是类的原型对象!!!!第二个是属性名字。通过拿到原型对象去加强该属性。而类装饰器拿到的参数是类的构造函数,不是原型对象
function logClass(param:any){
console.log(param);
# 类的原型对象,属性名
return function(target:any, name:string) {
console.log('target': target);
console.log(name);
target[name] = param;
}
}
class A {
@logClass("BBBB")
public bb: string | undefined;
constructor() {}
getData() {
console.log('getData');
}
}
# 可以看到动态扩展属性方法已经成功了
const a = new A(); // BBBB
a.getData(); // target:function A(){} bb
4. 方法装饰器
方法装饰器会被应用到方法的属性描述符上,可以用来监视,修改或者替换方法定义。
方法装饰器接受三个参数:
- 对于静态方法来说是类的构造函数;对于实例方法则是类的原型
- 方法名
- 成员的属性描述符
function logClass(param:any){
console.log(param);
# 类的原型对象,方法名,成员的属性描述符
return function(target:any, name:string, desc:any) {
console.log('target': target);
console.log('name':name);
console.log('desc':desc);
}
}
class A {
constructor() {}
@logClass("实例成员方法装饰器")
getDataA() {
console.log('getDataA');
}
@logClass("静态成员方法装饰器")
static getDataB() {
console.log('getDataB');
}
}
# 可以看到静态成员拿到的是构造函数,实例成员拿到的是类的原型对象
const a = new A();
a.getData();
5. 方法参数装饰器
可以使用参数装饰器为类的原型增加一个数据,也可以加强和修改对应的形参。传入下列三个参数:
1 跟方法装饰器一样,静态传构造,实例传原型。
2 参数的名字
3 参数在方法中的索引(第几位)
function logClass(param:any){
console.log(param);
# 类的原型对象,方法名,参数在方法中的索引
return function(target:any, name:string, index:any) {
console.log('target': target);
console.log('name':name);
console.log('index':index);
}
}
class A {
constructor() {}
@logClass("实例成员方法装饰器")
getDataA(
a:any,
b:andy,
@logClass("实例成员方法装饰器") c: any
) {
console.log('getDataA');
}
static getDataB(
a:any,
b:andy,
@logClass("静态成员方法装饰器") c: any
) {
console.log('getDataB');
}
}
# 装饰器(一般是函数)通俗的就是用来加强其修饰的对象,其修饰的对象通过参数传入其中,再在装饰器中执行一系列操作,扩展对象的功能
const a = new A();
a.getData(1,2,3,4);
总结,装饰器的执行顺序:即属性装饰器->方法装饰器->方法参数装饰器->类装饰器这