前言
2015年,微软Typescript团队和谷歌的Angular团队合作,其中Typescript提供了装饰器的特性
2016年,装饰器作为JS的新的特性提案被Yehuda Katz提出。
至今该特性还处于Stage 2的阶段。因此在JS中是不可以直接使用装饰器特性的,本文主要讨论在typescript中的装饰器。
装饰器是一种特殊的声明方式,它可以附属于类、属性、方法、变量、accessor。 在代码运行装饰器的时候,会带入被装饰的声明的信息。
在Typescript中,装饰器特性现在还是一个是实验性特性
如何定义装饰器
从实现的角度说,一个装饰器就是一个普通的方法,只是不同类型的装饰器其入参值不同,而在方法的内部,可以自由实现自己想要的逻辑。
接下来看看不同的装饰器具体是如何定义的?
类装饰器
/**
* 定义一个类装饰器
* @param {Function} constructor 被装饰的类的构造器
*/
function classDecorator(constructor: Function) {
// 对被装饰的类做各种操作
}
// -- 使用类装饰器 -- //
@classDecorator
class ClassToBeDecorated {}
方法装饰器
/**
* 定义一个方法装饰器
*
* @param {Function} target 如果装饰是静态方法,则为类的构造器(constructor); 如果被装饰的是实例方法,则为类的原型
* @param {string} propertyKey
* @param {PropertyDescriptor} descriptor 被装饰的方法的描述符
*/
function methodDecorator(target: Object, propertyKey: string, descriptor: PropertyDescriptor) {
}
// -- 使用方法装饰器 -- //
class ClassToBeDecorated {
@methodDecorator
methodToBeDecorated(){}
@methodDecorator
static staticMethodToBeDecorated() {}
}
存取器装饰器(Accessor Decorator)
/**
* 定义一个存取器装饰器
*
* @param {Function} target 如果装饰是静态方法,则为类的构造器(constructor); 如果被装饰的是实例方法,则为类的原型
* @param {string} propertyKey
* @param {PropertyDescriptor} descriptor 被装饰的方法的描述符
*/
function getterSetterDecorator(target: Object, propertyKey: string, descriptor: PropertyDescriptor) {
}
// -- 使用存取器装饰器 -- //
class ClassToBeDecorated {
@getterSetterDecorator
get getterToBeDecorated(){}
@getterSetterDecorator
set setterToBeDecorated() {}
}
属性装饰器(Property Decorator)
/**
* 定义一个属性装饰器
*
* @param {Function} target 如果装饰是静态属性,则为类的构造器(constructor); 如果被装饰的是实例属性,则为类的原型
* @param {string} propertyName 属性的名字
*/
function propertyDecorator(target: any, propertyName: string ) {
}
// -- 使用属性装饰器 -- //
class ClassToBeDecorated {
@propertyDecorator
public 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);
};
};