Decorators 装饰器
代码来自后盾网
装饰器为 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 20 21 22
| 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 18 19 20 21
| 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 32 33 34 35
| 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 7 8 9
| 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 23 24 25
| 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 7 8 9 10
| const ParamDecorator: ParameterDecorator = ( target: Object, propertyKey: string | symbol, parameterIndex: number ) => { console.log(target, propertyKey, parameterIndex); }; class User { show(id: number, @ParamDecorator user: { name: string }) {} }
|