TS:装饰器,最大的成就之一
1. 定义
- 装饰器:装饰器是一种特殊类型的声明,能够被附加到类声明,方法,属性或者参数上,可以修改类的行为;
- 装饰器本质是一个方法,可以注入到类、方法、属性参数上来拓展类、方法、属性、参数等功能;
- 常见的装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器;
- 装饰器分为两种:普通装饰器(不可传参)和装饰器工厂(可传参);
2. 类装饰器
-
类装饰器在类声明之前被声明,用于类构造函数,可以用来监控、修改、替换类的定义。
-
普通装饰器模式
// 类装饰器 function logClass(params : any){ // params就是当前类 console.log(params); params.prototype.url = '动态扩展的属性'; params.prototype.run = ()=>{ console.log('动态扩展方法'); } } @logClass class HttpClient { constructor() { } } let http : any= new HttpClient(); //打印 HttpClient {} console.log(http.url); //打印 动态扩展的属性 http.run(); // 打印 动态扩展的方法
-
装饰器工厂模式
function logClass(params:string){ return function(target : any){ //target为当前类 console.log(target); console.log(params); // params为参数 target.prototype.url = params; } } @logClass('hello') class HttpClient{ constructor(){ } } let http = new HttpClient(); console.log(http.url); // 打印 hello
-
重载类的构造函数
// 重载构造函数 function logClass(target){ console.log(target); return class extends target{ url : string = '我是修改后的url'; getData(){ console.log(this.url + '.'); } setData(api : string){ this.url = api; } } } @logClass class HttpClient{ public url : string | undefined; constructor(){ this.url = "我是构造函数里的url" } getData(){ console.log(this.url); } } let http : any = new HttpClient(); http.getData(); // 打印 我是修改后的数据
3. 属性装饰器
-
属性装饰器接收两个参数:
- 对于静态成员来说,是类的构造函数;对于实例成员来说,是类的原型对象;
- 成员名称;
// 类装饰器 function logClass(params:string){ return function(target : any){ //target为当前类 console.log(target); console.log(params); // params为参数 target.prototype.url = params; } } // 属性装饰器 function logProperty(params:any){ return function (target : any,attr : any){ console.log(target); // 类的原型对象 console.log(attr); // 属性的名称 target[attr] = params; } } @logClass('hello') class HttpClient{ @logProperty('heihei') public url : any | undefined; constructor(){ }; getData (){ console.log(this.url); } } let http = new HttpClient(); console.log(http.url); // 打印 heihei
4. 方法装饰器
-
同样的,方法装饰器可以用来监视、修改、替换方法的定义;
-
接收三个参数:
- 对于静态成员来说是类的构造函数,对于实例成员来说是原型对象;
- 成员名字;
- 成员的属性描述符;
// 方法装饰器 function getMethod(params : any){ return function(target:any,methodName : any,desc : any){ console.log(target); console.log(methodName); console.log(desc); target.name = 'xxx'; target.run = ()=>{ console.log('run'); } } } class HttpClient{ public url : string; constructor() { } @getMethod('http://www.baidu.com') getData(){ console.log(this.url); }; }
-
修改原方法:
// 方法装饰器 function getMethod(params : any){ return function(target:any,methodName : any,desc : any){ console.log(target); console.log(methodName); console.log(desc); target.name = 'xxx'; target.run = ()=>{ console.log('run'); } // 保存当前方法 let omethod = desc.value; desc.value = function (...args : any[]){ args = args.map((value)=>{ return String(value); }) console.log(args); // 在新替换的函数里执行原函数 omethod.apply(this,args); } } } class HttpClient{ public url : string; constructor() { } @getMethod('http://www.baidu.com') getData(...args : any[]){ console.log("我是getData里的方法"); }; } let http = new HttpClient(); http.getData('yivi');
5. 方法参数装饰器
-
方法参数装饰器表达式在运行时会被当作函数来调用,可以使用参数装饰器为类的原型增加一些元素数据:
- 对于静态成员来说是类的构造函数,对于实例成员来说是原型对象;
- 方法的名称;
- 参数在函数的参数列表的索引;
// 方法参数装饰器 function logParams(params : any){ return function (target : any,methodName : any,paramsIndex : any){ console.log(params); console.log(target); console.log(methodName); console.log(paramsIndex); target.api = params; } } class HttpClient { public url : string; constructor() { } getData(@logParams('uuid') uuid : any){ console.log(uuid); } } let http = new HttpClient(); http.getData('12345'); console.log(http.api);
6. 装饰器的执行顺序
-
属性装饰器>>方法装饰器>>方法参数装饰器>>类装饰器;
-
同类装饰器从下到上执行;
-
读者可以通过上面例子自行实现装饰器顺序实例。