装饰器与装饰者模式
装饰者模式是设计模式之一,比较官方的解释如下:
装饰者模式能够在不改变原对象自身的情况下,在程序运行期间给对象动态的添加职责
而装饰器则是装饰者模式的实践
用法
装饰器分为 类装饰器、成员装饰器、参数装饰器。
为什么有类装饰器而没有函数装饰器呢?
由于 function 存在变量提升,所以运行至装饰器时 target 指向 undefined
装饰器本质是高阶函数,通过相关参数和返回值动态改变原来的对象(类)
在 Typescript 中,通过@来使用定义的装饰器,JavaScript 目前还没有正式支持装饰器(ES2024有望加入)
所以这里我们采用 TS 演示
类装饰器
类装饰器接收一个参数(target),也就是 class 本身
作为类的装饰器函数返回值存在两种情况:
- void,也就是没有返回值,则不改变原来的类
- 新的类,则与原来的类脱离干系,实例是通过新类生成的(不建议这样使用)
// 定义
function classDecorator (target: object) {
}
// 使用
@classDecorator
class Test {
}
成员装饰器
成员装饰器接收三个参数
- target:
- 如果是静态成员(static)返回class
- 如果是实例成员(pubic)返回类的原型(prototype)
- key:当前属性名
- descriptor:属性描述符对象
没有返回值
// 定义
function propDecorator (
target: object
key: string,
descriptor: PropertyDescriptor
) {
}
// 使用
class Test {
@propDecorator
public propName: string
}
参数装饰器
参数装饰器接收三个参数
- target:
- 如果是静态成员(static)返回class
- 如果是实例成员(pubic)返回类的原型(prototype)
- methodName:当前方法名
- index:参数arguments下标
没有返回值
// 定义
function paramDecorator (
target: object
methodName: string,
index: number
) {
}
// 使用
class Test {
getName(@paramDecorator id: string) {}
}
实现
上面也提到了,装饰器本身是通过高阶函数实现的,所以多个装饰器修饰时,会类似于洋葱一样嵌套函数调用,所以推出@这样的语法糖
以成员装饰器为例,我们通过 tsc 将上面的代码转换成 js,来看看是怎么做的
// 定义
function propDecorator (
target: object
key: string,
descriptor: PropertyDescriptor
) {
}
// 使用
var Test = (function () {
function Test(){}
__decorate(
[propDecorator],
Test.prototype,
'propName'
)
return Test
})()
__decorate 函数:__decorate 是一个辅助函数,它用于应用装饰器。这个函数一般由编译器自动生成,但在纯 JavaScript 环境中,你可以手动定义它。
注!这里的 __decorate函数 第一个参数是数组,证明支持同时应用多个装饰器。