TypeScript裝飾器、裝飾器的執行順序

裝飾器

特殊的類型聲明,它能夠附加到類聲明,方法,屬性或參數上,可以修改類的行爲。

通俗的講裝飾器就是一個方法,可以注入到類、方法、屬性參數上來擴展類、屬性、方法、參數的功能。

常見的裝飾器有:類裝飾器、屬性裝飾器、方法裝飾器、參數裝飾器。

裝飾器的寫法:普通裝飾器(無參)、裝飾工廠(可傳參)

裝飾器是過去幾年中js 最大的成就之一,已是ES7的標準特徵之一。

普通類裝飾器

類裝飾器是在類聲明之前被聲明(緊靠着類聲明)。類裝飾器應用於類構造函數,可以用來監視,修改或者替換類定義。

//定義裝飾器
function logClass(params:any){
    console.log(params:any);
  //這裏的params 就是當前類
}

@logClass    //調用裝飾器
class HttpClient{
    constructor(){

    }
    getData(){

    }
}

  打印結果:                     

那麼已知是當前類,那麼就可以操作該類,可以擴展,修改這個類。

//定義裝飾器
function logClass(params:any){
    console.log(params:any);
  //這裏的params 就是當前類
    params.prototype.apiUrl = 'xxx';
    params.prototype.run = function{
        console.log("--------run---------");
    }
}

@logClass    //調用裝飾器
class HttpClient{
    constructor(){

    }
    getData(){

    }
}
var http:any = new HttpClient();
console.log(http.apiUrl);
http.run();

 帶參數的類裝飾器(裝飾器工廠)

//定義裝飾器工廠
function logClass(params:string){
    return function(target:any){
        console.log(target);
        console.log(params);
    }
    target.prototype.apiUrl = params; //屬性裝飾器
}

@logClass("www.baidu.com")    //調用裝飾器
class HttpClient{
    constructor(){

    }
    getData(){

    }
}

var  http:any = new HttpClient();
console.log(http.apiUrl);

   通過打印可以看到,將字符串hello 賦值給了params,將對象賦值給了target.

類裝飾器重載構造函數

裝飾器表達式會在運行時當做函數被調用,類的構造函數作爲其唯一的參數。

如果類裝飾器返回一個值,他會使用提供的構造函數來替換類的聲明。

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  http = new HttpClient();
http.getData();

屬性裝飾器

屬性裝飾表達式會在運行時當做函數被調用,傳入下列兩個參數:

  1. 對於靜態成員來說是類的構造函數,對於實例成員是類的原型對象。
  2. 成員的名字。
//類裝飾器
function logClass(params:string){
     
   return function(target:any){
        console.log(target);
        console.log(target);
    }

}

//屬性裝飾器
function logProperty(params:any){
    
        return function(target:any,attr:any){
            console.log(target);
            console.log(attr);
            target[attr] = params;
        } 
    }

@logClass('xxx')
class HttpClient{
   @logProperty("http://www.baidu.com")
   public apiUrl:string | undefined;
    constructor(){
      
    }
    getData(){
      console.log(this.apiUrl);
    }
}

屬性裝飾器 中打印的 target就是當前對象, 而attr 就是屬性 apiUrl,所以可以根據 

   target[attr] = params;
來修改屬性。

方法裝飾器

方法裝飾器它會被應用到方法的屬性描述符上,可以來監視,修改或者替換方法定義。

方法裝飾器會在運行時傳入下列3個參數:

1、對靜態成員來說是累的構造函數,對於實例成員是累的原型對象。

2、成員的名字。

3、成員的屬性描述符。

 

//方法裝飾器
function logmethod(params:any){
    return function(target:any,methodName:any,desc:any){
      console.log(target);
      console.log(methodName);
      console.log(desc);

      target.apiUrl = 'xxxx';
      target.run = function(){
            console.log(this.url);
        }
    }
}

class HttpClient{
    public apiUrl:string | undefined;
    constructor(){
      
    }

    @logmethod('www.baidu.com')
    getData(){

    }
}
var http:any = new HttpClient();
console.log(http:apiUrl);
http.run();

同理,target 還是這個原型對象,methodName就是getData 即方法名稱, desc是個對象,對象中的value 就是方法名稱。

 

演示:用方法裝飾器修改方法,將方法裝飾器裏面傳入的所有參數改爲string 類型。

原理:修改desc.value

//方法裝飾器
function logmethod(params:any){
    return function(target:any,methodName:any,desc:any){
      console.log(target);
      console.log(methodName);
      console.log(desc);
      // 1保存當前方法
        var oldMethod = desc.value;
        desc.value = function(...args:any[]){//用三點運算符來接收傳入的數組參數
            args = args.map((value)=>{
                return String(value);
    
            })  
            console.log(args);    
        }
     
    }
}

class HttpClient{
    public apiUrl:string | undefined;
    constructor(){
      
    }

    @logmethod('www.baidu.com')
    getData(){
        console.log("我是getData裏面的方法");
    }
}

var http = new HttpClient();
http.getData("123","xxx");

 

 

很明顯getData方法被重寫了。 

那麼如果想修改原來的getData方法那應該這麼寫

//方法裝飾器
function logmethod(params:any){
    return function(target:any,methodName:any,desc:any){
      console.log(target);
      console.log(methodName);
      console.log(desc);
      // 1保存當前方法
        var oldMethod = desc.value;
        desc.value = function(...args:any[]){//用三點運算符來接收傳入的數組參數
            args = args.map((value)=>{
                return String(value);
    
            })  
            console.log(args);
             oldMethod.apply(this,args);   
        }
     
    }
}

class HttpClient{
    public apiUrl:string | undefined;
    constructor(){
      
    }

    @logmethod('www.baidu.com')
    getData(){
        console.log("我是getData裏面的方法");
    }
}

var http = new HttpClient();
http.getData("123","xxx");


這裏的  oldMethod.apply(this,args);   中的this表示

 desc.value = function(...args:any[]){ } ,因爲是在當前方法中操作的,所以this 指代當前方法。

而args代表參數,它的整體含義就是,在新的方法中傳入參數然後調用原來的方法。
       

 

可以很明顯的看到,原來的getData方法被成功執行。

如果此時將...args:any[] 參數數組傳入原來的getData方法中即代碼如下

//方法裝飾器
function logmethod(params:any){
    return function(target:any,methodName:any,desc:any){
      console.log(target);
      console.log(methodName);
      console.log(desc);
      // 1保存當前方法
        var oldMethod = desc.value;
        desc.value = function(...args:any[]){//用三點運算符來接收傳入的數組參數
            args = args.map((value)=>{
                return String(value);
    
            })  
            console.log(args);
             oldMethod.apply(this,args);   
        }
     
    }
}

class HttpClient{
    public apiUrl:string | undefined;
    constructor(){
      
    }

    @logmethod('www.baidu.com')
    getData(...args:any[]){
      console.log(args:any);
      console.log("我是getData裏面的方法");
    }
}

var http = new HttpClient();
http.getData("123","xxx");


 

打印的結果如下

可以看到原來的方法中的參數全部變成了string 類型,

這就是方法裝飾器,它可以修改方法,也可以修改傳入參數類型。

方法參數裝飾器

參數裝飾器表達式會在運行時當做函數被調用,可以使用參數裝飾器爲類的原型增加一些數據,傳入下列三個參數:

1、對於靜態成員來說是類的構造函數,對於實例成員來說是類的原型對象。

2、參數的名字。

3、參數在函數參數列表中的索引。

//方法參數裝飾器
function logparams(params:any){
  
      return function(target:any,paramsName:any,paamsIndex:any){
          console.log(params);
          console.log(target);
          console.log(paramsName);
          console.log(paamsIndex);

       }
}

class HttpClient{
    public apiUrl:string | undefined;
    constructor(){
      
    }

    getData(@logparams('xxxxx'uuid:any)){
      console.log("我是getData裏面的方法");
    }
}

var http = new HttpClient();
http.getData(123456);


uuid成功被打印。

裝飾器執行順序

//定義裝飾器工廠

function logClass1(params:string){
    return function(target:any){
        console.log('類裝飾器1');
    }
}

function logClass2(params:string){
    return function(target:any){
        console.log('類裝飾器2');
    }
}

//屬性裝飾器
function logProperty(params?:any){
 
        return function(target:any,attr:any){
            console.log('屬性裝飾器');
        } 
    }


//方法裝飾器
function logmethod(params:any){
    return function(target:any,methodName:any,desc:any){
      console.log('方法裝飾器');
    }
}

//方法參數裝飾器
function logparams1(params:any){
  
      return function(target:any,paramsName:any,paamsIndex:any){
          console.log('方法參數裝飾器1');
       }
}

function logparams2(params:any){
  
      return function(target:any,paramsName:any,paamsIndex:any){
          console.log('方法參數裝飾器2');
       }
}


@logClass1("www.baidu.com")
@logClass2("www.IMOOC.com")
class HttpClient{
   @logProperty()//可以不傳參
   public apiUrl:string | undefined;
    constructor(){
      
    }
    @logmethod('www.baidu.com')
    getData(){
      console.log(this.apiUrl);
    }
   setData(@logparams1('xxxxx'uuid:any),@logparams2('xxxxx'uuid:any)){
      console.log(this.apiUrl);
    }

}



var  http:any = new HttpClient();

裝飾器執行順序

屬性》方法》方法參數》類

如果有多個同樣的裝飾器,它會先執行後面的裝飾器。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章