前言
装饰器: 装饰器是一种特殊类型声明, 它能够被附加到类声明,方法,属性或者参数上, 可以修改类的行为
通俗的讲装饰器就是一个方法, 可以注入到类,方法,属性参数上来扩展类,属性,方法,参数功能
常见的装饰器:属性装饰器,方法装饰器,参数装饰器
写法: 普通修饰器(无法传参)、装饰器工厂(可以传参)
类修饰器
装饰器在类声明之前被执行,类装饰器应用于类构造函数,可以用来监视,修改或者代替类定义
类普通装饰器:
function logClass(params: any) { console.log(params); } @logClass class HttpClient{ constructor() { } getData() { } }
定义了一个 logClass 类 并且带了一个默认参数 params
这个 params == HttpClinent
function logClass(params: any) { console.log(params); params.prototype.httpUrl = "http.xxxxx"; // 通过原型链添加一个 给类添加一个属性 } @logClass class HttpClient{ constructor() { } getData() { } } var httpClient = new HttpClient(); console.log(httpClient.httpUrl);
运行结果:
通过 类装饰器添加 方法
function logClass(params: any) { console.log(params); params.prototype.httpUrl = "http.xxxxx"; // 通过原型链添加一个 给类添加一个属性 params.prototype.run = function() { console.log("run-app"); } } @logClass class HttpClient{ constructor() { } getData() { } } var httpClient = new HttpClient(); console.log(httpClient.httpUrl); httpClient.run(); // 执行通过装饰器添加的方法
类装饰器工厂:
function logClass(params: string) { console.log(params); // 传入的参数类型 return function(tarage:any) { // tarage == HttpClient } } @logClass("传入参数") class HttpClient{ constructor() { } getData() { } } var httpClient = new HttpClient();
如果是装饰器工厂必须要写入 参数 @logClass("传入参数")
可以根据装饰器传入的参数去实现需要的功能
function logClass(params: string) { console.log(params); // 传入的参数类型 return function(tarage:any) { // tarage == HttpClient tarage.prototype.apiUrl = params; // 通过原型链修改 类的属性 } } @logClass("http://xxxxxx123") class HttpClient{ constructor() { } getData() { } } var httpClient = new HttpClient();
而且还可以重载 构造函数
function logClass(target: any) { console.log(target); return class extends target{ apiUrl:any="修改后的数据"; getData() { this.apiUrl = this.apiUrl + "---===--"; console.log(this.apiUrl); } } } @logClass class HttpClient{ public apiUrl: string | undefined; constructor() { this.apiUrl = "我是构造函数里面的apiUrl"; } getData() { console.log(this.apiUrl); } } var httpClient = new HttpClient(); console.log(httpClient.apiUrl) httpClient.getData();
属性装饰器
属性装饰器表达式会在运行时当作函数调用, 传入2个参数
* 第一个参数:对于静态成员来说 是类的构造函数, 对于实例化成员是类的原型对象
* 第二个参数:成员名字
function logProperty(params: any) { console.log("接收传入的参数", params); return function(tarage: any, attr: any) { console.log("参数一:", tarage); // 对于实例化成员是类的原型对象 console.log("参数二:", attr);// 成员名字 } } class HttpClient{ @logProperty("test") public url:string|undefined; // 这个属性使用 属性装饰器 constructor() { } getData() { } }
通过参数一 修改属性值:
function logProperty(params: any) { console.log("接收传入的参数", params); return function(tarage: any, attr: any) { console.log("参数一:", tarage); // 对于实例化成员是类的原型对象 console.log("参数二:", attr);// 成员名字 tarage[attr] = params; // 这里修改属性值 } } class HttpClient{ @logProperty("test") public url:string|undefined; // 这个属性使用 属性装饰器 constructor() { } getData() { console.log( "url属性值", this.url); } } var httpClient = new HttpClient(); httpClient.getData(); // 输出 url属性值 test
方法装饰器
它会被应用到方法的 属性描述符上, 可以用来监视,修改或者替换方法定义
* 方法装饰器会在运行时传入3个参数
* 参数一: 对于静态成员来说是类的构造函数,对于实例成员是类的原型
* 参数二: 成员名字
* 参数三: 成员的属性描述符。
function logMethod(params:any) { console.log("调用传入的参数", params) return function(tarage:any, methodName: string, desc:any){ console.log("参数一", tarage); // 实例成员是类的原型 console.log("参数二", methodName); // 成员名字, 方法名 console.log("参数三", desc); // 描述 } } class HttpClient{ public url:string|undefined; constructor() { } @logMethod("get") getData() { // 这个方法使用了 装饰器 console.log( "url属性值", this.url); } }
第一个参数是 实例成员的原型, 可以通过它给原型添加属性或方法
function logMethod(params:any) { console.log("调用传入的参数", params) return function(tarage:any, methodName: string, desc:any){ console.log("参数一", tarage); // 实例成员是类的原型 console.log("参数二", methodName); // 成员名字, 方法名 console.log("参数三", desc); // 描述 tarage.api = "我是装饰器添加的 属性"; tarage.run = function() { console.log("我是装饰器添加的方法"); } } } class HttpClient{ public url:string|undefined; constructor() { } @logMethod("get") getData() { // 这个方法使用了 装饰器 console.log( "url属性值", this.url); } } var httpClient = new HttpClient(); console.log(httpClient.api); // 输出: 我是装饰器添加的 属性 httpClient.run(); // 输出 我是装饰器添加的方法
第三个参数是 描述
修改装饰器方法, 把装饰器方法里面的所有参数修改为string类型
function logMethod(params:any) { console.log("调用传入的参数", params) return function(tarage:any, methodName: string, desc:any){ console.log(desc.value); // 输出 function() { console.log( "url属性值", this.url);} var methodFun = desc.value; desc.value = function(...args:any) { // 重写 getData 方法 args = args.map((value:any)=>{ return String(value); }); console.log(args); // 使用对象冒充, 还原之前的函数 methodFun.apply(this, args); // 执行输出 我是getData方法 } } } class HttpClient{ public url:string|undefined; constructor() { } @logMethod("get") getData(...args:any) { // 这个方法使用了 装饰器 console.log(args);// 输出: Array(2) ["123", "adb"] console.log( "我是getData方法"); } } var httpclient = new HttpClient(); httpclient.getData(123, "adb"); // 输出: Array(2) ["123", "adb"]
方法参数装饰器
参数装饰器表达式会在运行时当作函数调用,可以使用参数装饰器为类的原型
增加一些元素数据,传入三个参数:
参数一: 对于静态成员来说是类的构造函数, 对于实例成员是类的原型对象
参数二:方法名称
参数三:参数在函数参数列表中的索引
function logParams(params:any) { return function(target:any,methodName:any,paramsIndex:number){ console.log(params);// 我是装饰器参数 console.log(target); // Object {getData: , constructor: } console.log(methodName); // getData console.log(paramsIndex);// 0 } } class HttpClient{ public url:string|undefined; constructor() { } getData(@logParams("我是装饰器参数")uuid:any) { // 这个方法使用了 装饰器 console.log(uuid); console.log( "我是getData方法"); } } var httpClinet = new HttpClient(); httpClinet.getData("test");
注意装饰器的执行顺序:
属性装饰器>方法装饰器>方法参数装饰器>类装饰器
如果一个方法有两个装饰器, 那么 后面的装饰器先执行,再到前面的