装饰器
大约 2 分钟约 733 字
装饰器是什么
- 它是一个表达式
- 该表达式被执行后,返回一个函数
- 函数的入参分别为 target、name 和 descriptor
- 执行该函数后,可能返回 descriptor 对象,用于配置 target 对象
装饰器的分类
- 类装饰器(Class decorators)
- 属性装饰器(Property decorators)
- 方法装饰器(Method decorators)
- 参数装饰器(Parameter decorators)
类装饰器
类装饰器声明:
declare type ClassDecorator = <TFunction extends Function>(
target: TFunction
) => TFunction | void
类装饰器顾名思义,就是用来装饰类的。它接收一个参数:
- target: TFunction - 被装饰的类
function Greeter(target: Function): void {
target.prototype.greet = function (): void {
console.log('Hello Semlinker!')
}
}
@Greeter
class Greeting {
constructor() {
// 内部实现
}
}
let myGreeting = new Greeting()
myGreeting.greet() // console output: 'Hello Semlinker!';
上面的例子中,我们定义了 Greeter 类装饰器,同时我们使用了 @Greeter 语法糖,来使用装饰器。
function Greeter(greeting: string) {
return function (target: Function) {
target.prototype.greet = function (): void {
console.log(greeting)
}
}
}
@Greeter('Hello TS!')
class Greeting {
constructor() {
// 内部实现
}
}
let myGreeting = new Greeting()
myGreeting.greet() // console output: 'Hello TS!';
属性装饰器
属性装饰器声明:
declare type PropertyDecorator = (
target: Object,
propertyKey: string | symbol
) => void
属性装饰器顾名思义,用来装饰类的属性。它接收两个参数:
- target: Object - 被装饰的类
- propertyKey: string | symbol - 被装饰类的属性名
function logProperty(target: any, key: string) {
delete target[key]
const backingField = '_' + key
Object.defineProperty(target, backingField, {
writable: true,
enumerable: true,
configurable: true,
})
// property getter
const getter = function (this: any) {
const currVal = this[backingField]
console.log(`Get: ${key} => ${currVal}`)
return currVal
}
// property setter
const setter = function (this: any, newVal: any) {
console.log(`Set: ${key} => ${newVal}`)
this[backingField] = newVal
}
// Create new property with getter and setter
Object.defineProperty(target, key, {
get: getter,
set: setter,
enumerable: true,
configurable: true,
})
}
class Person {
@logProperty
public name: string
constructor(name: string) {
this.name = name
}
}
const p1 = new Person('semlinker')
p1.name = 'kakuqo'
以上代码我们定义了一个 logProperty 函数,来跟踪用户对属性的操作,当代码成功运行后,在控制台会输出以下结果:
Set: (name) => semlinker
Set: (name) => kakuqo
方法装饰器
方法装饰器声明:
declare type MethodDecorator = <T>(
target: Object,
propertyKey: string | symbol,
descriptor: TypePropertyDescript<T>
) => TypedPropertyDescriptor<T> | void
方法装饰器顾名思义,用来装饰类的方法。它接收三个参数:
- target: Object - 被装饰的类
- propertyKey: string | symbol - 方法名
- descriptor: TypePropertyDescript - 属性描述符
function LogOutput(tarage: Function, key: string, descriptor: any) {
let originalMethod = descriptor.value;
let newMethod = function(...args: any[]): any {
let result: any = originalMethod.apply(this, args);
if(!this.loggedOutput) {
this.loggedOutput = new Array<any>();
}
this.loggedOutput.push({
method: key,
parameters: args,
output: result,
timestamp: new Date()
});
return result;
};
descriptor.value = newMethod;
}
class Calculator {
@LogOutput
double (num: number): number {
return num * 2;
}
}
let calc = new Calculator();
calc.double(11);
// console ouput: [{method: "double", output: 22, ...}]
console.log(calc.loggedOutput);
参数装饰器
参数装饰器声明:
declare type ParameterDecorator = (target: Object, propertyKey: string | symbol,
parameterIndex: number ) => void
参数装饰器顾名思义,是用来装饰函数参数,它接收三个参数:
- target: Object - 被装饰的类
- propertyKey: string | symbol - 方法名
- parameterIndex: number - 方法中参数的索引值
function Log(target: Function, key: string, parameterIndex: number) {
let functionLogged = key || target.prototype.constructor.name;
console.log(`The parameter in position ${parameterIndex} at ${functionLogged} has
been decorated`);
}
class Greeter {
greeting: string;
constructor(@Log phrase: string) {
this.greeting = phrase;
}
}
// console output: The parameter in position 0
// at Greeter has been decorated