代码来自后盾网
装饰器为 ts 提供了强大的代码复用功能
使用装饰器需要在 tsconfig.json 中修改
装饰器是试验性的功能,需要在配置文件中开启
tsconfig.json1 2
| "experimentalDecorators": true, "emitDecoratorMetadata": true
|
类装饰器 ClassDecorator
相当于在原型对象上添加属性
类装饰器只有一个参数,是构造函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const moveDecorator: ClassDecorator = (target: Function) => { console.log(target) target.prototype.getPosition = (): { x: number; y: number } => { return { x: 20, y: 20 } } }
@moveDecorator class Player {}
@moveDecorator class Tank {}
const player = new Player() console.log(player.getPosition())
|
装饰器语法糖 @
不使用语法糖,需要传递类到对应的函数中。使用语法糖不需要手动调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const moveDecorator: ClassDecorator = (target: Function) => { console.log(target) target.prototype.getPosition = (): { x: number; y: number } => { return { x: 20, y: 20 } } }
class Player {} moveDecorator(Player)
@moveDecorator class Tank {}
const player = new Player() console.log(player.getPosition())
|
装饰器叠加
可以使用多个装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| const moveDecorator: ClassDecorator = (target: Function) => { target.prototype.getPosition = () => { console.log("获取位置") } } const MusicDecorator: ClassDecorator = (target: Function) => { target.prototype.playMusic = () => { console.log("音乐播放") } }
@moveDecorator class Player {}
const player = new Player() player.getPosition()
@MusicDecorator @moveDecorator class Tank {}
const tank = new Tank() tank.playMusic() tank.getPosition()
|
实例:全局消息响应
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const MessageDecorator: ClassDecorator = (target: Function) => { target.prototype.sendMessage = (message: string, type: "success" | "error" = "success") => { return { message, type, } } }
@MessageDecorator class LoginController { login() { console.log("登录业务处理") setTimeout(() => { this.sendMessage("登录成功") }, 2000) } } 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
| const MusicDecorator = (type: string): ClassDecorator => { switch (type) { case "player": return (target: Function) => { target.prototype.playMusic = () => { console.log("play player music") } } case "tank": return (target: Function) => { target.prototype.playMusic = () => { console.log("play tank music") } } default: return (target: Function) => { target.prototype.playMusic = () => { console.log("play other music") } } } }
@MusicDecorator("player") class Player {} new Player().playMusic()
@MusicDecorator("tank") class Tank {} new Tank().playMusic()
|
方法装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| const ShowDecorator: MethodDecorator = (...args: any[]) => { console.log(args)
} class User { @ShowDecorator public show() {} }
|
可以使用函数装饰器更改函数的内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const ShowDecorator: MethodDecorator = (...args: any[]) => { console.log(args) args[2].value = () => { console.log("ShowDecorator changed") } } class User { @ShowDecorator public show() { console.log("show function ") } }
new User().show()
|
上面那样使用数组接收后调用不是很直观,我们可以使用变量名接收方法装饰器参数
const ShowDecorator: MethodDecorator = (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => { ... };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const ShowDecorator: MethodDecorator = ( target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor ) => { descriptor.value = () => { console.log("ShowDecorator changed") } } class User { @ShowDecorator public show() { console.log("show function ") } }
new User().show()
|
调用装饰器时,也可以更改静态方法的内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const ShowDecorator: MethodDecorator = ( target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor ) => { descriptor.value = () => { console.log("ShowDecorator changed") } } class User { @ShowDecorator public static show() { console.log("show function ") } }
User.show()
|
调用 writable = true
控制方法不能重新声明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const ShowDecorator: MethodDecorator = ( target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor ) => { descriptor.value = () => { console.log("ShowDecorator changed") } descriptor.writable = false } class User { @ShowDecorator public static show() { console.log("show function ") } }
User.show() User.show = () => { console.log("重写show方法") } User.show()
|
示例:模拟代码高亮
可以先使用变量将函数内容保存起来,在自定义操作后直接调用保存的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const HighlightDecorator: MethodDecorator = ( target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor ) => { const method = descriptor.value descriptor.value = () => { return `<div style="background: red;">${method()}</div>` } } class User { @HighlightDecorator public show() { return "js code " } } new User().show()
|
示例:结合装饰器工厂实现延迟执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const SleepDecorator = (times: number = 2000): MethodDecorator => { return (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => { const method = descriptor.value descriptor.value = () => { setTimeout(() => { method() }, times) } } } class User { @SleepDecorator(3000) public show() { console.log("wxw") } } new User().show()
|
示例:全局异常处理
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 ErrorDecorator: MethodDecorator = ( target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor ) => { const method = descriptor.value
descriptor.value = () => { try { method() } catch (e) { console.log("%c异常处理", "color: green;") console.log(`%c${e}`, "color: red") } } }
class User { @ErrorDecorator find() { throw new Error("出错了") } } new User().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 26 27 28 29 30 31
| type UserType = { name: string; isLogin: boolean; permissions: string } const user = { name: "wxw", isLogin: true, permissions: ["admin", "member"], } const AccessDecorator = (keys: string[]): MethodDecorator => { return (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => { const method = descriptor.value const validate = () => keys.every((k) => user.permissions.includes(k)) descriptor.value = () => { if (user.isLogin && validate() === true) { console.log("验证通过") method() return } else { console.log("验证失败") return false } } } }
class User { @AccessDecorator(["admin"]) find() { console.log("find function") } } new User().find()
|
属性装饰器
接收的参数
- args[0]:
target
静态参数就是构造函数,普通参数就是其原型对象
- args[1]:
propertyKey
属性名称
- args[2]: undefined
1 2 3 4 5 6
| const PropDecorator: PropertyDecorator = (target: Object, propertyKey: string | symbol) => { console.log(target, propertyKey) } class User { public username: string }
|
实例:使用属性装饰器动态改变对象属性
将属性改为全部大写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const UpperDecorator: PropertyDecorator = (target: Object, propertyKey: string | symbol) => { let value: string = undefined
Object.defineProperty(target, propertyKey, { get() { return value.toUpperCase() }, set(v) { value = v }, }) }
class Article { @UpperDecorator public title: string | undefined }
const article = new Article() article.title = "article title" console.log(article.title)
|
参数装饰器
接收的参数
- args[0]:
target
静态参数就是构造函数,普通参数就是其原型对象
- args[1]:
propertyKey
属性名称
- args[2]:
parameterIndex
参数所在的位置,从 0 开始计数
1 2 3 4 5 6
| const ParamDecorator: ParameterDecorator = (target: Object, propertyKey: string | symbol, parameterIndex: number) => { console.log(target, propertyKey, parameterIndex) } class User { show(id: number, @ParamDecorator user: { name: string }) {} }
|