代码来自后盾网
装饰器为 ts 提供了强大的代码复用功能
使用装饰器需要在 tsconfig.json 中修改
装饰器是试验性的功能,需要在配置文件中开启
{
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
}
类装饰器 ClassDecorator
相当于在原型对象上添加属性
类装饰器只有一个参数,是构造函数。
const moveDecorator: ClassDecorator = (target: () => void) => {
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())
装饰器语法糖 @
不使用语法糖,需要传递类到对应的函数中。使用语法糖不需要手动调用。
const moveDecorator: ClassDecorator = (target: () => void) => {
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())
装饰器叠加
可以使用多个装饰器
const moveDecorator: ClassDecorator = (target: () => void) => {
target.prototype.getPosition = () => {
console.log("获取位置")
}
}
const MusicDecorator: ClassDecorator = (target: () => void) => {
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()
实例:全局消息响应
const MessageDecorator: ClassDecorator = (target: () => void) => {
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()
装饰器工厂
根据不同的条件返回不同的装饰器
function MusicDecorator(type: string): ClassDecorator {
switch (type) {
case "player":
return (target: () => void) => {
target.prototype.playMusic = () => {
console.log("play player music")
}
}
case "tank":
return (target: () => void) => {
target.prototype.playMusic = () => {
console.log("play tank music")
}
}
default:
return (target: () => void) => {
target.prototype.playMusic = () => {
console.log("play other music")
}
}
}
}
@MusicDecorator("player")
class Player {}
new Player().playMusic()
@MusicDecorator("tank")
class Tank {}
new Tank().playMusic()
方法装饰器
const ShowDecorator: MethodDecorator = (...args: any[]) => {
console.log(args)
/**
打印参数:
[0]: 如果是静态函数就是构造函数,如果是普通方法就是其原型对象
[1]: 使用装饰器的方法名称
[2]: 方法属性的描述
[
User: {},
"show",
{
"value": [Function (anonymous)] // 函数的内容
"writable": true,
"enumerable": false,
"configurable": true
}
]
*/
}
class User {
@ShowDecorator
public show() {}
}
可以使用函数装饰器更改函数的内容
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() // 打印 "ShowDecorator changed",函数内容已经被更改
上面那样使用数组接收后调用不是很直观,我们可以使用变量名接收方法装饰器参数
const ShowDecorator: MethodDecorator = (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => { ... };
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() // 打印 "ShowDecorator changed",函数内容已经被更改
调用装饰器时,也可以更改静态方法的内容
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() // 打印 "ShowDecorator changed",函数内容已经被更改
调用 writable = true
控制方法不能重新声明
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() // 打印 "ShowDecorator changed",函数内容已经被更改
User.show = () => {
console.log("重写show方法")
}
User.show()
示例:模拟代码高亮
可以先使用变量将函数内容保存起来,在自定义操作后直接调用保存的函数
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()
示例:结合装饰器工厂实现延迟执行
function 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()
示例:全局异常处理
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()
示例:根据权限限制访问
可以根据传入的权限数组进行判断用户是否有访问权限
interface UserType { name: string, isLogin: boolean, permissions: string }
const user = {
name: "wxw",
isLogin: true,
permissions: ["admin", "member"],
}
function 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()
}
else {
console.log("验证失败")
return false
}
}
}
}
class User {
@AccessDecorator(["admin"])
find() {
console.log("find function")
}
}
new User().find()
属性装饰器
接收的参数
- args0:
target
静态参数就是构造函数,普通参数就是其原型对象 - args1:
propertyKey
属性名称 - args2: undefined
const PropDecorator: PropertyDecorator = (target: object, propertyKey: string | symbol) => {
console.log(target, propertyKey)
}
class User {
public username: string
}
实例:使用属性装饰器动态改变对象属性
将属性改为全部大写
const UpperDecorator: PropertyDecorator = (target: object, propertyKey: string | symbol) => {
let value: string
// 在这里使用Object.defineProperty对原数据进行更改
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)
参数装饰器
接收的参数
- args0:
target
静态参数就是构造函数,普通参数就是其原型对象 - args1:
propertyKey
属性名称 - args2:
parameterIndex
参数所在的位置,从 0 开始计数
const ParamDecorator: ParameterDecorator = (target: object, propertyKey: string | symbol, parameterIndex: number) => {
console.log(target, propertyKey, parameterIndex)
}
class User {
show(id: number, @ParamDecorator user: { name: string }) {}
}
装饰器执行顺序:
参数装饰器 -> 方法装饰器
评论区
评论加载中...