随着TypeScript和ES6里引入了类,在一些场景下我们需要额外的特性来支持标注或修改类及其成员。 装饰器(Decorators)为我们在类的声明及成员上通过元编程语法添加标注提供了一种方式。 Javascript里的装饰器目前处在 建议征集的第二阶段,但在TypeScript里已做为一项实验性特性予以支持。
注意 装饰器是一项实验性特性,在未来的版本中可能会发生改变。
若要启用实验性的装饰器特性,你必须在命令行或tsconfig.json里启用experimentalDecorators编译器选项:
命令行:
tsc --target ES5 --experimentalDecorators
装饰器
装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上。 装饰器使用 @expression这种形式,expression求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。
类装饰器ClassDecorator
首先定义一个类
class Http{
}
定义一个类装饰器函数,target作为参数,它的值就是构造函数
const Base: ClassDecorator = (target) => {
target.prototype.zl='zl';
target.prototype.fn=()=>{
console.log('zzz');
}
}
使用的时候 直接通过@函数名使用
@Base
class Http {
}
let http = new Http() as any;
console.log(http.zl);
http.fn();
装饰器工厂
其实也就是一个高阶函数 外层的函数接受值 里层的函数最终接受类的构造函数,利用函数柯里化或者闭包
比如Base装饰器需要传递参数
const Base = (name: string) => {
const f: ClassDecorator = (target) => {
target.prototype.zl = name
target.prototype.fn = () => {
console.log('zzz');
}
}
return f;
}
@Base('wo shi zl')
class Http {
}
let http = new Http() as any;
console.log(http.zl);
http.fn();
方法装饰器 MethodDecorator PropertyDescriptor
返回三个参数
- 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
- 成员的名字。
- 成员的属性描述符。
const Get = (url: string) => {
const fn: MethodDecorator = (target, propertyKey, descriptor) => {
// target:原型对象 | propertyKey:方法名称,比如getList | descriptor:描述
console.log(target, propertyKey, descriptor);
}
return fn
}
@Base('wo shi zl')
class Http {
@Get('https://api.apiopen.top/api/getHaoKanVideo?page=0&size=10')
getList(data: any) {
console.log(data);
}
}
let http = new Http() as any;
http.fn();
输出结果
target:{}
propertyKey:getList
descriptor:{
value: [Function: getList],
writable: true,
enumerable: false,
configurable: true
}
可以看出value就是一个函数,值为getList
输出:
现在调用一下接口。descriptor.value(res.data)实际上就是相当于调用getList方法并传参数res.data=data
const Get = (url: string) => {
const fn: MethodDecorator = (target, propertyKey, descriptor: PropertyDescriptor) => {
// target:原型对象 | propertyKey:方法名称,比如getList | descriptor:描述
console.log(target, propertyKey, descriptor);
axios.get(url).then((res) => {
descriptor.value(res.data)
})
}
return fn
}
@Base('wo shi zl')
class Http {
@Get('https://api.apiopen.top/api/getHaoKanVideo?page=0&size=10')
getList(data: any) {
console.log(data.result.list);
}
}
let http = new Http() as any;
http.fn();
参数装饰器 ParameterDecorator
返回三个参数
- 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
- 成员的名字。
- 参数在函数参数列表中的索引。
const Result=()=>{
const fn:ParameterDecorator=(target, propertyKey, parameterIndex)=>{
console.log(target, propertyKey, parameterIndex);
}
return fn;
}
@Base('wo shi zl')
class Http {
@Get('https://api.apiopen.top/api/getHaoKanVideo?page=0&size=10')
getList(@Result() data: any) {
// console.log(data.result.list);
}
}
输出:
比如我们想要在getList方法中直接输出data的值,就相当于data是已经解析出来的data.result
需要使用一个库reflect-metadata
npm i reflect-metadata -D
可以快速存储元数据然后在用到的地方取出来 defineMetadata getMetadata
const Get = (url: string) => {
const fn: MethodDecorator = (target, propertyKey, descriptor: PropertyDescriptor) => {
// target:原型对象 | propertyKey:方法名称,比如getList | descriptor:描述
// console.log(target, propertyKey, descriptor);
axios.get(url).then((res) => {
const key = Reflect.getMetadata('key', target)
descriptor.value(key ? res.data[key] : res.data)
})
}
return fn
}
const Result = () => {
const fn: ParameterDecorator = (target, propertyKey, parameterIndex) => {
// console.log(target, propertyKey, parameterIndex);
Reflect.defineMetadata('key', 'result', target);
}
return fn;
}
@Base('wo shi zl')
class Http {
@Get('https://api.apiopen.top/api/getHaoKanVideo?page=0&size=10')
getList(@Result() data: any) {
// console.log(data.result.list);
console.log(data);
}
}
属性装饰器
返回两个参数
- 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
- 属性的名字。
const Name: PropertyDecorator = (target, propertyKey) => {
console.log(target, propertyKey);
}
@Base('wo shi zl')
class Http {
@Name
name: string
@Get('https://api.apiopen.top/api/getHaoKanVideo?page=0&size=10')
getList(@Result() data: any) {
// console.log(data.result.list);
// console.log(data);
}
}
let http = new Http() as any;
http.fn();
输出: