# 装饰器
装饰器是一项实验性特性,若要启用实验性的装饰器特性,你必须在命令行或tsconfig.json
里启用experimentalDecorators
编译器选项。
{
"compilerOptions": {
"experimentalDecorators": true
}
}
# 装饰器定义
装饰器是一种特殊类型的函数,可以用来修饰类,属性和方法。可以在不修改类,属性和方法的前提下扩展类,属性和方法的功能。
装饰器使用@expression
这种形式,expression
求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。
// 定义一个装饰器
function persons(target){
console.log('target',target);
}
// 在类的上面使用装饰器
@persons
class PersonClass{}
在上面的代码中,person
函数作为一个装饰器,如果我们想要使用装饰器,只需要在对应的类上面,使用@person
注意,这里来的person
必须是一个函数。
# 装饰器的功能——扩展类的功能
我们使用装饰器的最大的特点就是为了拓展类的功能,装饰器中的参数target
就是要装饰的类PersonClass(){}
,接下来我们尝试给类拓展功能。示例:
// 定义一个装饰器
function persons(target){
// 给装饰器修饰的类拓展方法
target.prototype.name = "装饰器";
target.prototype.showName = function(){
return this.name;
}
}
@persons
class PersonClass{}
let p1 = new PersonClass();
console.log(p1.name); // 装饰器
console.log(p1.showName()); //装饰器
在上面的代码中,我们给装饰器修饰的类拓展了一个属性name
和方法showName
。实例化以后我们发现可以正常地调用这些属性和方法。这就相当于拓展了类的功能。
# 装饰器运行时机
装饰器对类的行为的改变,是代码编译时发生的(不是TypeScript编译,而是js在执行机中编译阶段),而不是在运行时。这意味着,装饰器能在编译阶段运行代码。也就是说,装饰器本质就是编译时执行的函数。
# 装饰器的分类
刚刚在上面中,我们使用的装饰器都是简单的装饰器,但是事实上如果我们需要拓展类的功能,通常需要传入一些参数,这时候就需要有参装饰器了。在typecript
中通过装饰器工厂来实现装饰器参数的传递。
function func(value){
return (target) => {
console.log(value);
console.log(target)
}
};
@func('hello')
class PersonClass{}
从上面的代码中,我们给装饰器传递了参数@func('hello')
。func
是一个工厂函数,它可以接收参数,同时返回一个函数。返回的函数作为装饰器。
# 属性装饰器
属性装饰器声明在一个属性声明之前(紧靠着属性声明)。
function prop(value:string|number){
return (target:any,attr:any):void => {
target[attr] = value;
}
};
class Index{
@prop('hello')
static sex:string;
@prop(27)
public age:number;
constructor(){};
}
let index = new Index();
console.log(Index.sex); // hello
console.log(index.age); // 27
- 如果是属性装饰器,那么声明在属性声明之前。
- 属性装饰器有两个参数,
target
和attr
。如果修饰的是静态属性,那么target
是类,如果修饰的是实例属性,那么target
是实例成员。attr
是对应的属性。
# 方法装饰器
属性装饰器声明在一个方法声明之前(紧靠着方法声明)。方法装饰器接收三个参数。
- 对于静态方法来说是类的构造函数,对于实例方法是类的原型对象。
- 方法名称
- 方法的属性描述符
{writable: true, enumerable: true, configurable: true, value: ƒ}
function get(){
return (target:any, methodName:string, desc:any) => {
console.log(target);
console.log(methodName);
console.log(desc);
// 修改原来的方法
desc.value = (arr) =>{
const str = arr.join();
return str;
}
}
}
class Http{
@get()
get(arr){
return arr;
}
}
# 总结
装饰器的特点
- 本质是一个函数
- 装饰器的功能就是用于拓展类的功能,比如给类的成员,方法等拓展功能
- 在编译时执行,而不是类实例话或者调用时执行
- 装饰器的使用,关键是看需要几个参数,然后根据需要修饰的对象来实现功能即可