邂逅TypeScript之装饰器

304 阅读3分钟

1.装饰器的概念

装饰器是一种特殊类型的声明,它能够被附加到类声明、方法、属性或参数上,可以修改类的行为。 通俗的讲装饰器就是一个函数/方法,可以注入到类、方法、属性、参数上来扩展类、属性、方法、参数的功能。 在Java中,常常被叫作注解。

装饰器本质就是一个函数,但它是一个返回函数的函数,它所带来的好处就是不需要关心原有函数的逻辑,加上装饰器就自动帮你做点特定的事情。

2.手写装饰器

2.1 属性装饰器

我们在组件里定义一个变量 result,并用一个表情装饰器去修饰。

@Emoji() public result = 'When will the epidemic end in Shanghai?';

在html模板中去使用

<p>{{ result }}</p>

接下来就是手写这个表情装饰器了,可以新建一个文件夹 decorators,并在该文件夹下创建文件 index.ts

export function Emoji() {  
    // target 是装饰器的目标对象,此处即为组件; key 是应用装饰器的变量,此处为 result  
    return (target: object, key: string) => {      
        let val = target[key];     
        const getter = () => {       
            return val;     
        }     
        const setter = (value: string) => {       
            val = `😂 ${value} 😂`     
        }     
        // 附加到原来对象上     
        Object.defineProperty(target, key, {       
            get: getter,       
            set: setter,       
            enumerable: true,       
            configurable: true     
        })  
    }
}

为什么要这样一个类型,如果它是一个返回函数的函数,Emoji 想作用于 result 这个属性的话,首先要知道位于哪个类当中,然后再去找该属性。所以参数 target 对应的就是这个类,然后去找它的 result 属性,任何一个类在某种角度上来讲和字典/索引对象很像,这些属性都可以看作其里面的索引值。Emojs 函数写好后,再在组件中将写好的装饰器导入进来。

2.2 方法装饰器

在html模板中写一个click事件

<p (click)="handleClick()">Hello TypeScript!</p>

在组件中写一个确认对话框的装饰器:

@Confirmable('确认要执行吗?')
public handleClick(): void {    
    console.log('点击已执行'); 
}

同样在文件夹 decorators下的文件index.ts中手写装饰器

export function Confirmable(message: string) {  
    return (
        target: object, 
        key: string, 
        descriptor: PropertyDecorator
    ) => {    
        // 先把装饰器修饰的函数赋给一个变量    
        const original = descriptor.value;    
        // 然后替换成下面的函数    
        descriptor.value = function(...args: any[]) {      
            // 执行弹出对话框      
            const allow = window.confirm(message);      
            // 如果确认则执行原有函数逻辑      
            if (allow) {        
                const result = original.apply(this, args);        
                return result;      
            }      
            return null;    
        }    
        return descriptor;  
    }
}

descriptor 就是对一个属性的描述

PropertyDecorator 这个类型也是 JavaScript 中的一个概念,在一个组件类当中,对于每一个属性都有一个描述符,里面有该属性的值、set、get等。因为装饰器修饰的是函数 handleClick,所以在 index.ts 中先将原来的函数放到一个变量里,然后对其进行处理。为什么要进行处理?

因为要弹出一个对话框,根据对话框选择的情况决定是否执行原来的函数。首先将属性描述符 descriptor 里的 value 替换掉(这个 value 其实就是一个函数),换上一个匿名函数,在其中执行弹出对话框的操作,弹出要有信息,正好装饰器里有接收一个参数 message,对应的就是装饰器里写的字符串。根据返回结果(布尔值)决定是否执行原有的函数。

为什么最后要返回 descriptor?

应用到方法的装饰器就是这样规定的,具体可以看一下 typescript 文档 www.typescriptlang.org/docs/handbo…

一般情况下,使用框架提供的装饰器就可以了。那么什么场景需要使用装饰器?由于装饰器就是一个函数,所以我们使用装饰器往往是需要复用某些代码逻辑,但是如果单纯是复用代码,普通函数也可以,那么装饰器的优势其实是它提供了一种类似配置的方式实现复用,这样会降低门槛,让团队协作起来更容易,也更规范。但是请注意,装饰器不要滥用,装饰器满天飞之后就失去了意义。