TypeScript学习: 十二、TS中的装饰器

前言

装饰器: 装饰器是一种特殊类型声明, 它能够被附加到类声明,方法,属性或者参数上, 可以修改类的行为 通俗的讲装饰器就是一个方法, 可以注入到类,方法,属性参数上来扩展类,属性,方法,参数功能 常见的装饰器:属性装饰器,方法装饰器,参数装饰器 写法: 普通修饰器(无法传参)、装饰器工厂(可以传参)  

类修饰器

装饰器在类声明之前被执行,类装饰器应用于类构造函数,可以用来监视,修改或者代替类定义  

类普通装饰器:

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);

运行结果:

TypeScript学习: 十二、TS中的装饰器

 

 通过 类装饰器添加 方法

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");

 

 注意装饰器的执行顺序:

属性装饰器>方法装饰器>方法参数装饰器>类装饰器

如果一个方法有两个装饰器, 那么 后面的装饰器先执行,再到前面的

上一篇:TypeScript高级用法


下一篇:[搭建CLI效率工具] Rollup + TypeScript 搭建CLI工程