08、TypeScript 装饰器
1.类装饰器
/* 装饰器:装饰器是一种特殊类型的声明,它能够被附加到类声明、方法、属性或参数上,可以修改类的行为 通俗的讲装饰器就是一个方法,可以注入到类、方法、属性参数上来扩展类、属性、方法、参数的功能 常见的装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器 装饰器的写法:普通装饰器(无法传参)、装饰器工厂(可以传参) 装饰器是过去几年中 js 最大的成就之一,也是 es7 的标准特性之一 */ // 类装饰器:类装饰器在类声明之前被声明(紧靠着类声明)。 // 类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。传入一个参数 // 定义一个装饰器(普通装饰器) function logClass(params: any) { // params 就是指当前类 console.log(params); // 给这个类扩展一个属性 url params.prototype.url = '动态扩展的属性'; } // 声明一个类 // 使用装饰器(普通装饰器),可以扩展类的属性和方法,在不修改类的前提下,扩展类的功能 @logClass class HttpClient { name: string; constructor(name: string) { this.name = name; } getData(): void { } } // 实例化,这里加上 any ,h.url 就不会报错了 var h: any = new HttpClient('zhangning'); console.log(h.url); // 类装饰器工厂(可以传参) function logClass1(params: string) {// params 表示传递的参数 return function(target: any) {// 这里的参数 target 指的是当前的类 console.log(params);// hellow console.log(target);// HttpClient(){} target.prototype.url = params; }; } @logClass1('hellow') class HttpClient1 { constructor() { } getData() { } } let h1: any = new HttpClient1(); console.log(h1.url);
重载构造函数例子
// 重载构造函数的例子, // 类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。 // 如果类装饰器返回一个值,他会使用提供的构造函数来替换类的声明 // 定义装饰器,在装饰器中修改构造函数(就是重载构造函数) function logClass11(target: any) { console.log(target); return class extends target { url: any = '我是装饰器中修改的数据'; getData(){ console.log(this.url) } }; } // 类 @logClass11 class HttpClient11 { url: string | undefined; constructor() { this.url = '我是构造函数中的url'; } getData(): void { console.log(this.url); } } let h11 = new HttpClient11(); h11.getData();// 我是构造函数中的url
2.属性装饰器
/* 属性装饰器表达式会在运行时当作函数被调用,传入下列 2 个参数: 1.对于静态成员来说是类的构造函数,对于实例成员是类的原型对象 2.成员的名字 */ // 定义类装饰器 function logClass(params: any) { return function(target: any) { console.log(target); console.log(params); }; } // 定义属性装饰器 function logProperty(params: any) { // target:类的原型对象,attr:属性的名称,这里的 attr 就是 url return function(target: any, attr: any) { console.log(target); console.log(attr); // target 相当于 类装饰器中的 target.prototype target[attr] = params;// 这里就把 HttpClient 中的 url 改成了 '属性装饰器传递的数据' }; } @logClass('xxxx') class HttpClient { @logProperty('属性装饰器传递的数据') public url: any | undefined; constructor() { } getData() { } }
3.方法装饰器
/* 方法装饰器:它会被应用到方法的 属性描述符上,可以用来监视,修改或者替换方法定义 方法装饰会在运行时传入下列三个参数: 1.对于静态成员来说是类的构造函数,对于实例成员是类的原型对象 2.成员的名字 3.成员的属性描述符 */ // 定义方法装饰器 function logFun(params: any) { // 1.对于静态成员来说是类的构造函数,对于实例成员是类的原型对象 // 2.成员的名字 // 3.成员的属性描述符 return function(target: any, methodName: any, desc: any) { console.log(target); console.log(methodName); console.log(desc); // 可以扩展属性 target.name = 'zhangning'; target.run = function() { console.log('跑步'); }; // 修改装饰器的方法 把装饰器方法里面传入的所有参数改为 string 类型 // 1.保存当前的方法 let oMethos = desc.value; desc.value = function(...args: any[]) { args = args.map(value => { return String(value); }); console.log(args);// args 就是实例传递过来的数组 oMethos.apply(this, args); }; }; } class HttpClient { public url: any | undefined; constructor() { } @logFun('类装饰器传递参数') getData(...args: Array<any>): void { console.log(args);// args 就是实例传过来的数组 console.log(this.url); } }
4.方法参数装饰器
/* 参数装饰器: 参数装饰器表达式会在运行时当作函数被调用,可以使用参数装饰器为类的原型增加一些元素数据,传入下列三个参数: 1.对于静态成员来说是类的构造函数,对于实例成员是类的原型对象 2.方法的名字 3.参数在函数参数列表中的索引 */ function logProp(params: any) { return function(target: any, methodName: any, paramsIndex: any) { console.log(params); console.log(target); console.log(methodName); console.log(paramsIndex); target.name = params; }; } class HttpClient { public url: any | undefined; constructor() { } getData(@logProp('uuid')uuid: any): void { console.log(uuid); } } let hc: any = new HttpClient(); hc.getData(123); console.log(hc.name);
5.装饰器的执行顺序
// 装饰器的执行顺序 function logClass1(params: string) { return function(target: any) { console.log('类装饰器1'); }; } function logClass2(params: string) { return function(target: any) { console.log('类装饰器2'); }; } function logAttribute(params?: string) { return function(target: any, attrname: any) { console.log('属性装饰器'); }; } function logMethod(params?: string) { return function(target: any, attrname: any, desc: any) { console.log('方法装饰器'); }; } function logparams1(params?: string) { return function(target: any, attrname: any, desc: any) { console.log('方法参数装饰器1'); }; } function logparams2(params?: string) { return function(target: any, attrname: any, desc: any) { console.log('方法参数装饰器2'); }; } // 4.最后执行类装饰器,先执行下面的,再执行上面的 @logClass1('zhangning') @logClass2('xxx') class HttpClient { // 1.首先执行属性装饰器 @logAttribute() public url: string | undefined; constructor() { } // 2.其次执行方法装饰器 @logMethod() getData() { return true; } // 3.接着执行方法参数装饰器,执行顺序是从后往前 setData(@logparams1() attr1: any, @logparams2() attr2: any) { } } let hc: any = new HttpClient(); /* 打印结果: 属性装饰器 方法装饰器 方法参数装饰器2 方法参数装饰器1 类装饰器2 类装饰器1 */