装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上。
装饰器使用 @expression这种形式,expression求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。
通俗的理解可以认为就是在原有代码外层包装了一层处理逻辑。个人认为装饰器是一种解决方案,而并非是狭义的@Decorator,后者仅仅是一个语法糖罢了。
装饰器在身边的例子随处可见,一个简单的例子:水龙头上装起泡器,把空气混入水流中,很多泡泡在水里。但是安装起泡器对水龙头本身并没有什么影响,即使拆掉起泡器,水龙头也能照样工作。水龙头的作用在于阀门的控制,至于水中掺不掺气泡不是水龙头需要关心的。相当于就是给水龙头增加一个附加功能。
1、为什么要用装饰器有些时候,我们会对传入参数的类型判断、对返回值的排序、过滤,对函数添加节流、防抖或其他的功能性代码,基于多个类的继承,各种各样的与函数逻辑本身无关的、重复性的代码。所以,对于装饰器,可以简单地理解为是非侵入式的行为修改。
2、如何定义装饰器
装饰器本身其实就是一个函数,理论上忽略参数的话,任何函数都可以当做装饰器使用。
// helloword.ts function helloWord(target: any) { console.log('hello Word!'); } @helloWord class HelloWordClass { // ...... }
3、装饰器组合:多个装饰器可以同时应用到一个声明上,就像下面的示例:
// 书写在同一行上: @f @g x // 书写在多行上: @f @g x
修饰器对类的行为的改变,是代码编译时发生的(不是TypeScript编译,而是js在执行机中编译阶段),而不是在运行时。这意味着,修饰器能在编译阶段运行代码。也就是说,修饰器本质就是编译时执行的函数。在Node.js环境中模块一加载时就会执行。
但是实际场景中,有时希望向装饰器传入一些参数,如下:
@Path("/hello", "world") class HelloService {}
此时上面装饰器方法就不满足了(VSCode编译报错),这是我们可以借助JavaScript中函数柯里化特性
function Path(p1: string, p2: string) { return function (target) { // 这才是真正装饰器 // do something } }
装饰器的类型有:类装饰器、访问器装饰器、属性装饰器、方法装饰器、参数装饰器,但是没有函数装饰器(function)。