什么是装饰器
- 装饰器是一种特殊的语法,允许开发者在类声明、方法、属性或参数上添加注解或修改其行为。这种功能提供了一种方式来实现元编程,使得代码更具可读性和可维护性
- 简单总结来说就是
- 持有被装饰对象
- 增强被装饰对象
装饰器类型
- 类装饰器
- 属性装饰器
- 方法装饰器
- 访问器装饰器
- 参数装饰器
执行顺序:类装饰器 → 属性装饰器 → 方法装饰器 → 访问器装饰器 → 参数装饰器。
装饰器写法
- 普通装饰器(无法传参)
- 装饰器工厂(可以传参)
类装饰器
- 用于类的构造函数,可以用于修改类的行为或添加元数据。
- 接收一个参数,构造函数
普通装饰器
const decorator:ClassDecorator = (target:any) => {
console.log(target===service,target)
target.prototype.name='bang'
}
//等同于decorator(s)
@decorator
class service{
constructor() { }
getName(){}
}
const s:any = new service()
console.log(s.name)
- 但是上述例子无法接受传参
那么如何传参呢,那就是装饰器工厂
装饰器工厂
- 装饰器工厂很简单,是一个函数的同时,也返回一个函数。也就是函数柯里化
- 第一个参数是用户自定义的参数
- 第二个参数接收装饰器本来的那些参数
//params作为用户自定义的参数,其实装饰器工厂就是包了一层
const decorator = (params: string) => {
//target为装饰器本来的参数
return (target: any) => {
console.log(target === service, target, params);
target.prototype.name = "bang";
};
};
@decorator("test")
class service {
constructor() {}
getName() {}
}
const s: any = new service();
console.log(s.name);
输出结果
true [class service] test
bang
下面普通装饰器和装饰器工厂直接在代码里举例,不具体划分了
属性装饰器
- 用于类的属性,可以用于添加元数据。
- 接收两个参数
- 一个是原型对象
- 一个是属性名称
//普通装饰器
const test: PropertyDecorator = (target: any, propertyKey: string | symbol) => {
console.log(target, propertyKey);
};
class Service {
@test
public name: string;
constructor() {
this.name = "bang";
}
}
输出结果
target:{} propertyKey:name
为什么target是{},而不是{name:'bang'}呢
因为name是实例属性,不是原型属性
//装饰器工厂
const test = (params: string) => {
return (target: any, propertyKey: string | symbol) => {
console.log(target, propertyKey, params);
};
};
class Service {
@test("ces")
public name: string;
constructor() {
this.name = "bang";
}
}
输出结果:
{} name ces
方法装饰器
- 用于类的方法,可以用于修改方法的定义或者添加一些额外的逻辑。
- 接收三个参数
- 原型对象
- 方法名称
- 函数对象描述符
- value:函数对象
- writable: 可写的
- enumerable: 可枚举的
- configurable: 可配置的
//普通装饰器
const decorator: MethodDecorator = (target, propertyKey, descriptor) => {
console.log("target:",target,"propertyKey:",propertyKey,"descriptor:",descriptor);
};
class Service {
public name: string;
constructor() {
this.name = "bang";
}
@decorator
getName() {}
}
输出结果
target: {}
propertyKey: getName
descriptor: {
value: [Function: getName],
writable: true,
enumerable: false,
configurable: true
}
//装饰器工厂
const decorator = (params: any) => {
return (target: any, propertyKey: any, descriptor: PropertyDescriptor) => {
console.log(params);
console.log("target:",target,"propertyKey:",propertyKey,"descriptor:",descriptor);
};
};
class Service {
public name: string;
constructor() {
this.name = "bang";
}
@decorator("ces")
getName() {}
}
输出结果:
ces
target: {}
propertyKey: getName
descriptor: {
value: [Function: getName],
writable: true,
enumerable: false,
configurable: true
}
参数装饰器
- 用于方法参数,可以用于添加元数据。
- 接收三个参数
- 原型对象
- 方法名称
- 参数索引
//普通装饰器
const decorator: ParameterDecorator = (target,propertyKey,parameterIndex) => {
console.log('target:',target,'propertyKey:',propertyKey,'parameterIndex:',parameterIndex)
}
class test {
public name: string;
constructor() {
this.name = "bang";
}
getName(name: string, @decorator age: number) {}
}
输出结果:
target: {} propertyKey: getName parameterIndex: 1
//装饰器工厂
const decorator = (params: string) => {
return (target: any, propertyKey: any, parameterIndex: number) => {
console.log(params);
console.log('target:',target,'propertyKey:',propertyKey,'parameterIndex:',parameterIndex)
};
};
class test {
public name: string;
constructor() {
this.name = "bang";
}
getName(name: string, @decorator("ces") age: number) {}
}
输出结果:
ces
target: {} propertyKey: getName parameterIndex: 1
实现Get装饰器
使用装饰器工厂实现Get装饰器
import axios from "axios";
const Get = (url: string) => {
return (target: any, key: any, descriptor: PropertyDescriptor) => {
const fn = descriptor.value;
axios.get(url).then((res) => {
fn?.(res, {
status: 200,
success: true,
});
})
.catch((e) => {
fn?.(e, {
status: 500,
success: false,
});
});
};
};
class Controls {
public name: string;
constructor() {
this.name = "bang";
}
//调用一下掘金热榜的接口
@Get(
"https://api.juejin.cn/content_api/v1/content/article_rank?category_id=6809637767543259144&type=hot&aid=2608&uuid=7410977319672890932&spider=0"
)
getData(res: any, status: any) {
console.log("status:", status);
//处理一下数据方便展示
console.dir(
res?.data?.data?.map((a: any) => a.content),
{ depth: null }
);
}
}