最近在学习Nest框架时,发现大量使用了装饰器。所以,我们需要掌握修饰器的基本用法才能更好的理解Nest的写法。
装饰器(Decorator) 是一种特殊的函数,用于修改类、方法、属性或参数的行为。
类装饰器
类装饰器可以动态修改类的行为。
@withCreatedTime
class A {
name: string
constructor(name: string) {
this.name = name
}
}
function withCreatedTime(target: Function) {
target.prototype.createdAt = new Date().getTime()
}
const a = new A('A')
console.log((a as any).createdAt) // 有效的时间戳
装饰器@withCreatedTime会给类A的实例添加createdAt属性。
类方法装饰器
用于对类的方法进行拦截,如添加日志、权限校验等。
注意:需要启用
experimentalDecorators选项。
class A {
name: string
constructor(name: string) {
this.name = name
}
@log
sayName() {
console.log('My name is ${this.name}')
}
}
function log(target: Object, propertyKey: string, descriptor: PropertyDescriptor): PropertyDescriptor {
const originMethod = descriptor.value
descriptor.value = function(...args: any[]) {
console.log(`A.prototype.${propertyKey} to be called!`)
return originMethod.apply(this, args)
}
return descriptor
}
const a = new A('A')
a.sayName()
// 输出:
// A.prototype.sayName to be called!
// My name is A
装饰器@log拦截sayName方法,在执行前输出日志信息。
注意:修饰器不能用于函数。
类属性装饰器
同类方法装饰器,这里就不重复叙述了。
参数装饰器
用于拦截方法的参数,可以自动填充默认值等。
注意:需要启用
experimentalDecorators选项和emitDecoratorMetadata选项。
class A {
name: string
constructor(name: string) {
this.name = name
}
@applyDefaultParams
echo(@defaultParams('localhost') host?: string, @defaultParams('Hello') message?: string) {
console.log(`${host} say: ${message}`)
}
}
function defaultParams(value: any) {
return function (target: Object, propertyKey: string, parameterIndex: number) {
const existingParams = Reflect.getOwnMetadata('defaultParams', target, propertyKey) || []
existingParams[parameterIndex] = value
Reflect.defineMetadata('defaultParams', existingParams, target, propertyKey)
}
}
function applyDefaultParams(target: Object, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value
descriptor.value = function (...args: any[]) {
const defaultParams: any[] = Reflect.getOwnMetadata('defaultParams', target, propertyKey) || []
args = defaultParams.map((value, index) => (args[index] === undefined ? value : args[index]))
return originalMethod.apply(this, args)
}
return descriptor
}
const a = new A('A')
a.echo()
a.echo('127.0.0.1')
a.echo('0.0.0.0', 'Hi')
// 输出:
// localhost say: Hello
// 127.0.0.1 say: Hello
// 0.0.0.0 say: Hi
装饰器@applyDefaultParams拦截方法,在执行前获取默认参数后传递给原方法;装饰器@defaultParams拦截方法参数,存储默认参数值,便于方法装饰器获取。
注意:
- 此示例需要引入reflect-metadata库,用于装饰器相关的元数据存取。
- 不能在参数装饰器里面直接通过
target[propertyKey]修改方法的行为,因为它实际上并没有修改到descriptor.value的值。