装饰器语法虽然还处于提案阶段,但是确实能够在实际应用中方便的解决一些问题,近期看了一下TypeScript中装饰器的一些基本用法,做了一些笔记如下.
类装饰器
装饰器配置
装饰器是一个函数
装饰器属于一个新特性,需要在tsconfig.json中进行配置才能正常使用:
1 2 3
| experimentalDecorators: true, emitDecoratorMetadata: true
|
装饰器的种类有很多,比如类装饰器ClassDecorator、方法装饰器MethodDecorator、属性装饰器PropertyDecorator,参数装饰器ParameterDecorator等
装饰器的定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
const moveDecorator: ClassDecorator = function(target: Function){ console.log(target) target.prototype.getPosition = ():{x:number, y: number} => { return {x: 100, y: 200} } }
@moveDecorator class Tank { public getPosition(){} } const t = new Tank() t.getPosition()
@moveDecorator class Player { public getPosition(){} } const p = new Player() p.getPosition()
|
装饰器的@decorator是一个语法糖,下边的两种方法得到的结果是等价的
只是语法糖看起来更加明了,知道修饰的是什么,而不用去查找函数的调用
这是类装饰器的原理,其他装饰器不是的,比如方法装饰器是Object.defineProperty()
1 2 3 4 5 6
| function decorator(target: object){}
@decorator class Tank{}
decorator(Tanke)
|
装饰器叠加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
const moveDecorator: ClassDecorator = function(target: Function){ console.log(target) target.prototype.getPosition = ():{x:number, y: number} => { return {x: 100, y: 200} } }
const musicDecorator: ClassDecorator = function(target: Function){ target.prototype.playMusic = (): void => { console.log('播放音乐') } }
@moveDecorator @musicDecorator class Tank { public getPosition(){} public playMusic(){} } const t = new Tank() console.log(t.getPosition()) t.playMusic()
|
装饰器实现统一的消息响应
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const MessageDecorator = function(target: Function){ target.prototype.message = (content: string) => { console.log(content) } }
@MessageDecorator class LoginController { public message(content:string){} public login(){ console.log('开始登录业务处理') this.message('登录成功啦') } } new LoginController().login()
|
装饰器工厂函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| const musicDecoratorFactory = (type: string): ClassDecorator => { switch(type){ case 'Tank': return (target: Function) => { target.prototype.playMusic = (): void => { console.log('坦克播放音乐') } } break; case 'Player': return (target: Function) => { target.prototype.playGame = (): void => { console.log('玩家玩游戏') } } break; default: return (target: Function) => { target.prototype.playMusic = (): void => { console.log('默认播放音乐') } } } }
@musicDecoratorFactory('Tank') class Tank { playMusic(){} } const T = new Tank() T.playMusic()
@musicDecoratorFactory('Player') class Player { playGame(){} } const P = new Player() P.playGame()
|
方法装饰器
装饰器只能用来装饰类和类的方法,因为函数存在函数提升的问题,所以不能用来装饰普通函数
修饰普通方法则target拿到原型对象
修饰静态方法则target拿到构造函数
方法装饰器是通过Object.defineProperty()实现的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const showDecorator: MethodDecorator = (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => { console.log(target, propertyKey, descriptor) }
class User { @showDecorator public show(){ console.log('hhhh') } }
|
静态方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| const Decorator: MethodDecorator = function(target: object, propertyKey: string | symbol, descriptor: PropertyDescriptor){ console.log(target) console.log(propertyKey) console.log(descriptor) descriptor.writable = false } class User { @Decorator public static show(){ console.log('hhh') } }
User.show()
|
对方法的返回内容进行修饰
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const highlightDecorator: MethodDecorator = function(target: object, propertyKey: string | symbol, descriptor: PropertyDescriptor){ const method = descriptor.value descriptor.value = () => { return `<span style="color:red;">${method()}</span>` } } class User { @highlightDecorator public response(){ return 'hhh.xxx.yyy' } } console.log(new User().response())
|
处理抛出错误
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const ErrorDecorator: MethodDecorator = (target: object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => { const method = descriptor.value descriptor.value = () => { try{ method() }catch(error){ console.log(`我抛出了一个错误,错误内容是${error}`) } } } class People { @ErrorDecorator public find(){ throw new Error('error') } }
new People().find()
|
属性装饰器与参数装饰器
属性装饰器根据修饰的属性是普通属性还是静态属性,分别能得到原型对象和构造函数
参数装饰器根据参数所在的方法是普通方法和静态方法,分别能得到方法的原型对象和构造函数
参数装饰器会比方法装饰器更先一步执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| const propDecorator: PropertyDecorator = (target: Object, propertyKey: string | symbol) => { console.log(target) console.log(propertyKey) }
const paramsDecorator: ParameterDecorator = (target: Object, propertyKey: string | symbol, parameterIndex: number) => { console.log(target) console.log(propertyKey) console.log(parameterIndex) } class HD { @propDecorator public name: string | undefined @propDecorator public static age: number
public say(id: number, @paramsDecorator content: string){} }
|
属性装饰器动态转换对象属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const propDecorator: PropertyDecorator = (target: Object, propertyKey: string | symbol) => { let value: string Object.defineProperty(target, propertyKey, { get: () => { return value.toLowerCase() }, set: (v) => { value = v } }) }
class HD { @propDecorator public title: string | undefined }
const obj = new HD() obj.title = "HELLO WORLD" console.log(obj.title)
|
元数据
元数据用于给对象的属性添加额外的描述信息
ts默认不支持元数据,需要引入reflect-metadata这个npm包才行
Reflect.defindMetadata用于设置元数据,接收四个参数:
[元数据属性名, 元数据属性值, 要设置的对象, 要设置的对象的属性]
Reflect.getMetadata用于查询元数据,接收三个参数:
[元数据的属性名, 对象名, 对象的属性名]
1 2 3 4 5 6 7 8
| import 'reflect-metadata' const hd = { name: 'alin' }
Reflect.defineMetadata('desc',{title:'hhh'}, hd, 'name')
console.log(Reflect.getMetadata('desc', hd, 'name'))
|
对参数做必传校验
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| import 'reflect-metadata'
const requiredDecorator: ParameterDecorator = (target: Object, propertyKey: string | symbol, parameterIndex: number) => { let requiredParams: number[] = [] requiredParams.push(parameterIndex) Reflect.defineMetadata('required', requiredParams, target, propertyKey) }
const validateDecorator: MethodDecorator = (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => { const method = descriptor.value descriptor.value = function(){ const requiredParams: number[] = Reflect.getMetadata('required', target, propertyKey) || [] requiredParams.forEach( function(index){ if(index > arguments.length || arguments[index] === undefined){ throw new Error('请传递必要的参数') } }) return method.apply(this, arguments) } }
class HD { @validateDecorator public find(name: string, @requiredDecorator id: number){ console.log(id) } }
|