介绍
类似与Java中的注解,可以用于类、域、以及方法或者方法的参数上。
在typescript中目前是一项实验性的特性,通过修改配置文件支持:
{
"compilerOptions":{
"experimentalDecorators": true
}
}
装饰器使用
装饰器使用@expression这种形式,expression求值后必须为一个函数。也就是说,@后面可以接一个表达式,只需要这个表达式的值是一个函数即可。
所以我们可以这样定义装饰器:
function Controller(constructor: Function) {
// 得到类的构造函数
}
@Controller
class User {}
当然这只是装饰器的一种形式,后面会给出装饰器用于类、方法、域、参数的不同形式及定义。
@expression的理解
既然expression是一个表达式,且这个表达式的值是一个函数,那么我可以想办法构造表达式啊。如下例:
function Controller(table: string) {
// 得到类的构造函数
reuturn function(constructor: Function){
}
}
@Controller("user")
class User {}
在这个示例里面Controller(“user”)是一个函数调用表达式,返回值是一个函数,ok,满足要求。假设我们要将这个类对应到数据库的表,不就可以通过这种方式进行表名的注入了吗。
更高级一点的,我们可以构造装饰器工厂:
function decoratorFactory(...params){
return function(target, property?,propertyDescriptor){
}
}
@decoratorFactory('param')
class user{}
这样可以实现定制装饰器或者根据参数返回不同的装饰器
多个装饰器
@f @g x
装饰器的求职顺序是f(g(x))
同一个类的装饰器求值顺序
- 参数装饰器=》方法装饰器=》属性装饰器=》类装饰器
总之就是范围从小到大
四类装饰器的使用
类装饰器
类装饰器应用于构造函数,可以用来监视、修改或替换类定义。类装饰器表达式会在运行时当作函数被调用,类的构造函数是唯一的参数。
function seal(constructor: Function) {
console.log(constructor.name)
}
@seal
class Greeter1 {
}
如果类装饰器返回一个新的函数,那么会使用这个新的函数替换原有类的声明
属性装饰器
先看一个例子
function format(component:any, format?:string){
console.log(component, format) // hello %s
return function(target:any,propertyKey:string){
Object.defineProperty(target,propertyKey,{
get: function(){
return '这是被拦截的消息'
}
})
console.log(target,propertyKey)
}
}
function seal(constructor: Function) {
console.log(constructor.name)
}
@seal
class Greeter1 {
@format('hello','%s')
property = "property";
hello: string = 'sub';
constructor(m:string){
this.hello = m;
}
}
console.log(new Greeter('hello').property) // "这是被拦截的属性"
属性装饰器有两个参数
- 对于实例属性,target是类的原型对象,对于静态成员是类的构造函数
- 成员的名字
注:在上例中,我们使用Object.defineProperty来定义了原型对象的property属性,并给出的get/set方法,所以在子对象中定义property是无效的,所有实例访问的都是原型上的property属性,详情可参考https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty#%E7%BB%A7%E6%89%BF%E5%B1%9E%E6%80%A7
方法装饰器
和属性装饰器几乎完全相同,区别是方法装饰器可以有第三个参数,为属性的描述符对象
function enumerable(value: boolean) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.enumerable = value;
};
}
参数装饰器
参数装饰器表达式会在运行时当作函数被调用,传入下列3个参数:
- 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
- 成员的名字。
- 参数在函数参数列表中的索引。
注意 参数装饰器只能用来监视一个方法的参数是否被传入。