前言
2015年,微软Typescript团队和谷歌的Angular团队合作,其中Typescript提供了装饰器的特性
2016年,装饰器作为JS的新的特性提案被Yehuda Katz提出。
至今该特性还处于Stage 2的阶段。因此在JS中是不可以直接使用装饰器特性的,本文主要讨论在typescript中的装饰器。
装饰器是一种特殊的声明方式,它可以附属于类、属性、方法、变量、accessor。 在代码运行装饰器的时候,会带入被装饰的声明的信息。
在Typescript中,装饰器特性现在还是一个是实验性特性
如何定义装饰器
从实现的角度说,一个装饰器就是一个普通的方法,只是不同类型的装饰器其入参值不同,而在方法的内部,可以自由实现自己想要的逻辑。
接下来看看不同的装饰器具体是如何定义的?
类装饰器
/*** 定义一个类装饰器* @param {Function} constructor 被装饰的类的构造器*/function classDecorator(constructor: Function) {// 对被装饰的类做各种操作}// -- 使用类装饰器 -- //@classDecoratorclass ClassToBeDecorated {}
方法装饰器
/*** 定义一个方法装饰器** @param {Function} target 如果装饰是静态方法,则为类的构造器(constructor); 如果被装饰的是实例方法,则为类的原型* @param {string} propertyKey* @param {PropertyDescriptor} descriptor 被装饰的方法的描述符*/function methodDecorator(target: Object, propertyKey: string, descriptor: PropertyDescriptor) {}// -- 使用方法装饰器 -- //class ClassToBeDecorated {@methodDecoratormethodToBeDecorated(){}@methodDecoratorstatic staticMethodToBeDecorated() {}}
存取器装饰器(Accessor Decorator)
/*** 定义一个存取器装饰器** @param {Function} target 如果装饰是静态方法,则为类的构造器(constructor); 如果被装饰的是实例方法,则为类的原型* @param {string} propertyKey* @param {PropertyDescriptor} descriptor 被装饰的方法的描述符*/function getterSetterDecorator(target: Object, propertyKey: string, descriptor: PropertyDescriptor) {}// -- 使用存取器装饰器 -- //class ClassToBeDecorated {@getterSetterDecoratorget getterToBeDecorated(){}@getterSetterDecoratorset setterToBeDecorated() {}}
属性装饰器(Property Decorator)
/*** 定义一个属性装饰器** @param {Function} target 如果装饰是静态属性,则为类的构造器(constructor); 如果被装饰的是实例属性,则为类的原型* @param {string} propertyName 属性的名字*/function propertyDecorator(target: any, propertyName: string ) {}// -- 使用属性装饰器 -- //class ClassToBeDecorated {@propertyDecoratorpublic propertyToBeDecoreated: string = '1';}
变量装饰器(Parameter Decorator)
/*** 定义一个变量装饰器** @param {Function} target 如果装饰是静态属性,则为类的构造器(constructor); 如果被装饰的是实例属性,则为类的原型* @param {string | symbol} key 变量参数的键值,通常为该变量的名字,也可以是symbol* @param {number} index 被装饰的变量在变量列表中的索引*/function parameterDecorator(target: Object, key: string | symbol, index: number) {}// -- 使用变量装饰器 -- //class ClassToBeDecorated {constructor(private @parameterDecorator parameterToBeDecorated: string) {}method(private @parameterDecorator parameterToBeDecorated: string) {}}
装饰器工厂
装饰器工厂就是生产装饰器函数的函数。
一个装饰器通常只完成某一个功能,为了使得装饰器支持根据传入的变量改变不同的功能,这个时候就需要装饰器工厂。
Typescript中的装饰器生成代码分析
const __decorate =// 检测__decorate方法是否存在,防止反复创建(this && this.__decorate) ||// decorators 装饰器列表function (decorators, target, key, desc) {// 从上文了解到不同的装饰器函数接收的参数数量是不同// 其中 【方法装饰器】 和 【存取器装饰器】的入参变量数量是3个,其中第三个参数是属性描述符// 这里判断参数数量大于3个的时候,通过getOwnPropertyDescriptor获取属性描述符const c = arguments.length;let r =c < 3? target: desc === null? (desc = Object.getOwnPropertyDescriptor(target, key)): desc;// 如果支持反射,那么直接使用反射对象提供的decorate方法// 如果不存在,那么就把装饰器函数作为普通的函数,把对应的参数传入。let d;if (typeof Reflect === 'object' && typeof Reflect.decorate === 'function') {r = Reflect.decorate(decorators, target, key, desc);} else {// 从后向前遍历装饰器并组合调用// 比如装饰器 @a @b @c 这样装饰顺序,那么就相当于这样的调用: a(b(c()))for (let i = decorators.length - 1; i >= 0; i--) {if ((d = decorators[i])) {r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;}}}return c > 3 && r && Object.defineProperty(target, key, r), r;};// 添加元数据const __metadata =(this && this.__metadata) ||function (k, v) {if (typeof Reflect === 'object' && typeof Reflect.metadata === 'function') {return Reflect.metadata(k, v);}};// 【变量装饰器】比较特殊,虽然都拥有3个参数,但是第3个参数不是属性描述符,而是变量的索引位置const __param =(this && this.__param) ||function (paramIndex, decorator) {return function (target, key) {decorator(target, key, paramIndex);};};
