puddlejumper26 / blogs

Personal Tech Blogs
4 stars 1 forks source link

09 Decorator #43

Open puddlejumper26 opened 4 years ago

puddlejumper26 commented 4 years ago

Decorator is a special class declaration, could be added to the method, attribution, and parameter.

Generally speaking, decorator is a method.

1.1 Class Decorator - common decorator - without passing parameters

function logClass(params: string){
    console.log(params)                 ====> params is current class
    params.prototype.apiUrl = 'xxxx';      ====> expand the attribution
    params.prototype.run = function () { console.log ('running';)};
}

@logClass
class HttpClient{
   constructor(){}
   getData(){}
}

// Instance
var http: any = new HttpClient();
console.log(http.apiUrl);    ====> 'xxxx'
http.run();    ====> 'running';

1.2 Class Decorator Factory - passing parameter

function logClass(params: string){
    return function (target: any) {
           console.log(target);               ====> get www.google.com
           console.log(params);             ====> get function HttpClient
           target.prototype.apiUrl = params;
    }
}

@logClass('www.google.com')
class HttpClient{
   constructor(){}
   getData(){}
}

// Instance
var http: any = new HttpClient();
console.log(http.apiUrl);    ====> 'www.google.com'

Reconstruct Constructor

class decorator will be passed as a function, and the constructor is the only parameter. If class decorator has a return, it will use the constructor for the class declaration.

function logClass(params: string){
    console.log(params)                 ====> params is current class
    params.prototype.apiUrl = 'xxxx';      ====> expand the attribution
    params.prototype.run = function () { console.log ('running';)};
}

@logClass
class HttpClient{
   public apiUrl :string | undefined;
   constructor(){
        this.apiUrl = 'constructor apiUrl';
    }
   getData(){
        console.log(this.apiUrl); 
    }
}

// Instance
var http: any = new HttpClient();
http.getData();    ====> 'constructor apiUrl';

Reconstructoring

function logClass(target: string){
    console.log(target)                 ====> params is current class
    return class extends target{
           apiUrl: any = 'modified data';
     }

    getData(){                                             ====> notice here must keep identical with HttpClient
         this.apiUrl = this.apiUrl + '---';
          console.log(this.apiUrl);            
     }
}

@logClass
class HttpClient{
   public apiUrl :string | undefined;
   constructor(){
        this.apiUrl = 'constructor apiUrl';
    }
   getData(){
        console.log(this.apiUrl); 
    }
}

// Instance
var http: any = new HttpClient();
http.getData();    ====> 'modified data---';

2.0 Property Decorator

It takes two parameters: constructor for static, prototype for instance

// Class decorator
function logClass(params: string){
    console.log(params)                 ====> params is current class
    params.prototype.apiUrl = 'xxxx';      ====> expand the attribution
    params.prototype.run = function () { console.log ('running';)};
}

// property decorator
function logProperty(params: any){    ====> params is 'xxxx'
    return function (target:any, attr:any){
         console.log(target);      ====> target is the class HttpClient
         console.log(attr);          ====> attr is the url, which is the attribution
         target.attr = params; 
    }
}

@logClass('xxxx')
class HttpClient{

   @logProperty('www.baidu.com')
   public url :string | undefined;
   constructor(){

    }
   getData(){
        console.log(this.url);
    }
}

// Instance
var http: any = new HttpClient();
http.getData();    ====> 'www.baidu.com';

3.1 Method Decorator - modify current instance property and method

passed with 3 parameters

function get(params:any){
    return function(target:any, methodName:any, desc:any){
            console.log(target);                ====> here is the getData function
            console.log(methodName);    ====> here is the name --> getData   
            console.log(desc);                   ====>  here is the detail of getData function

             target.apiUrl = 'xxx';
             target.run = function(){
                   console.log('run');
             }
     }
}

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

   @get('www.zhihu.com')
   getData(){
        console.log(this.apiUrl); 
    }
}

// Instance
var http: any = new HttpClient();
http.apiUrl;    ====> 'xxx';
http.run();      ====> 'run';

3.2 Method Decorator - modify method

function get(params:any){
    return function(target:any, methodName:any, desc:any){
            console.log(target);                ====> here is the getData function
            console.log(methodName);    ====> here is the name --> getData   
            console.log(desc.value);          

            // to change the method,  to add type string to all parameters 

            //1. Save current method
             var oMethod = desc.value;

              desc.value = function (...args: any[ ]){
                   args = args.map( (value) => {
                            return String(args);
                    })
                   console.log(args);

               /** oMethod.apply(this,args); ====> this is current function(...args:any[])
                                                             ====> args is the args inside the function
                                                             ====> this means oMethod is called inside this function
               **/
              }
     }
}

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

   @get('www.zhihu.com')
   getData(...args:any[]){
         /**console.log(args)**/              ====> ['123','xxx']
        console.log('this is getData method'); 
    }
}

// Instance
var http: any = new HttpClient();
http.getData(123,'xxx')                        =====> first print getData function
                                                            =====> then print ['123', 'xxx']
   // it means the getData function has already been replaced,
   // but if we want to just modify the getData function, then as following
   // new changes will be marked with  /**  **/

/** 
      http.getData(123,'xxx')                   =====> first print ['123', 'xxx']
                                                            =====> then print 'this is getData method'

**/

3.3 Method Parameter Decorator

passed with 3 parameters

function logParams(params:any){
     return function(target:any, methodName:any, paramsIndex:any){
           console.log(params);                                        ====> 'xxxx'
           console.log(target);                                          ====> getData function(){} 
           console.log(methodName);                              ====> getData   
           console.log(paramsIndex);                               ====>  0 

            target.apiUrl = params;
     }
}

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

   getData( @logParams('xxxx') uuid: any){
        console.log(uuid);                                          ====> 123456
    }
}

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

http.apiUrl;                                  ====> 'xxxx'

Orders of all Decorators

Attribute -> Method -> Method Params -> Class Multiple with Same, first from the latter ones


function logClass1(params:string){
        return function(target:any){
            console.log('class decorator 1');
        }
}

function logClass2(params:string){
        return function(target:any){
            console.log('class decorator 2');
        }
}

function logAttribute(params?:string){
        return function(target:any, attrName:any){
            console.log('Attribute decorator ');
        }
}

function logMethod(params?:string){
        return function(target:any, attrName:any, desc:any){
            console.log('Method decorator ');
        }
}

function logParams1(params?:string){
        return function(target:any, attrName:any, desc:any){
            console.log('Method Param decorator 1 ');
        }
}

function logParams2(params?:string){
        return function(target:any, attrName:any, desc:any){
            console.log('Method Param decorator 2 ');
        }
}

@logClass1('www.google.com')
@logClass2('xxx')
class HttpClient{

      @logAttribute()
      public apiUrl: string | undefined;
      constructor( ){

       }

       @logMethod()
       getData( ){
              return true;
        } 
        setData(@logParams1() attr1:any, @logParams2() attr2: any){

        }
}

var http:any = new HttpClient();

====> order is :   
Attribute decorator -> Method decorator -> Method Param decorator 2 
-> Method Param decorator 1 -> Class decorator 2 -> Class decorator 1