第一阶段.模块二:学习笔记-TypeSrcipt的装饰器

129 阅读4分钟

上一篇:第一阶段.模块二:学习笔记-TypeSrcipt的高阶特性二

装饰器

装饰器是一种特殊类型的声明,他能被附加到类声明,方法属性或参数上,可以修改类的行为

本质上就是一个方法,可以注入到类、方法、属性参数上来拓展类、属性、方法、参数的功能

常见的装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器

装饰器的写法:普通装饰器(无法传参)、装饰器工厂(可传参)

装饰器的执行顺序:从下到上执行的;如果是装饰器工厂,那么先从上到下执行装饰器工厂(装饰器工厂函数返回的是装饰器),再从下到上执行装饰器

类装饰器

类装饰器在类声明之前被声明(紧靠着类声明)

类装饰器应用于类构造函数,用来监视,修改或替换类定义

参数1:为装饰器装饰的构造函数

function logClass(params:any){
    console.log(params); // params为装饰的构造函数HttpClient
    params.prototype.apiUrl='动态拓展的属性' // 为构造函数HttpClient的原型添加属性
}

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

类的工厂装饰器

需要装饰器传参时必须在装饰器函数中return一个函数 此时返回的函数才是装饰器函数

参数1:为装饰器函数的传参

function logClass(params:string){
    return function(target:any){
        console.log(target); // target为装饰的构造函数HttpClient
        console.log(params); // params为装饰器的传参
    }	
}

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

类的重载

function logClass(target: any) {
  console.log(target); 
  return class extends target {
    apiUrl:any = '修改后的数据'
    getData(){
		console.log(this.apiUrl);
    }
  }
}

@logClass('xxx')
class HttpClient {
  public apiUrl:string | undefined
  constructor() {
    this.apiUrl = '构造函数中的apiUrl'
  }
  getData() {
    console.log(this.apiUrl);
  }
}

const http = new HttpClient();
http.getData();

通过装饰器中进行类继承构造函数可以实现类的重载

修改类中的属性方法并实现(原先类中的方法会被覆盖)

属性装饰器

属性装饰器表达式会在运行时当做构造函数被调用 传入两个参数

参数1:对于静态成员来说是类的构造函数,对于实例成员来说是类的原型对象
参数2: 成员的名字

function logProperty(params: any) {
  return function(target:any, attr:any) {
    console.log(target); // 构造函数
    console.log(attr);   //url --成员的命名
    target[attr] = params;    
  }
}


class HttpClient {
  @logProperty('xxx')
  public url: any | undefined;
  constructor() {
  }
  getData() {
    console.log(this.url); // 被装饰器修改后的属性 为 xxx
    
  }
}

const http = new HttpClient();
http.getData();

方法装饰器

应用到方法的属性描述符上,可以用来监视,修改或者替换方法定义

方法装饰器在运行时传入三个参数

参数1:对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
参数2:成员的名字
参数3:成员的属性描述符

function get(params: any) {
  return function(target:any, methodName:any, desc:any) {
    console.log(target);      // 构造函数
    console.log(methodName);  // 装饰器的方法名
    console.log(desc);        // 属性描述符的对象 desc.value为装饰的函数
    target.apiUrl = 'xxx';    // 为实例对象添加属性和方法
    target.run = function(){
      console.log('run');    
    } 
  }
}


class HttpClient {
  public url: any | undefined;
  constructor() {
  }
  @get('123')
  getData() {
    console.log(this.url);    
  }
}

const http = new HttpClient();
console.log(http.apiUrl);
http.run();

通过方法装饰器来拓展原方法的功能

function get(params: any) {
  return function(target:any, methodName:any, desc:any) {
    console.log(target);      
    console.log(methodName);  
    console.log(desc);        
    let oldMethod = desc.value  			 	// 将旧方法保存在oldMethod中
    desc.value = function(...argus:any[]){		// 将getData重新赋值并补充业务逻辑
      argus = argus.map((ele) => {
        return String(ele);
      })
      oldMethod.apply(this,argus)				// 调用新方法并将this绑定回实例对象
    }
  }
}


class HttpClient {
  public url: any | undefined;
  constructor() {
  }
  @get('123')
  getData(...argus:any[]) {
    console.log(argus);    						// 打印出装饰器处理过的argus
  }
}

const http = new HttpClient();
http.getData(123, 'xxx')

方法参数装饰器

参数装饰器表达式会在运行时当做函数被调用,可以使用参数装饰器为类的原型增加一些元素数据

调用时传入三个参数

参数1:对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
参数2:参数的名字
参数3:参数在函数参数列表中的索引

function logParams(params: any) {
  return function(target:any, methodName:any, paramsIndex:any) {
    console.log(params);  		// 装饰器的传参
    console.log(target);      	// 装饰器的构造函数
    console.log(methodName);  	// 装饰的方法名
    console.log(paramsIndex);	// 参数的索引 为0
  }
}
class HttpClient {
  public url: any | undefined;
  constructor() {
  }
  getData(@logParams('xxx') uuid:any) {
    console.log(uuid);    		// 打印出实参123
  }
}

const http = new HttpClient();
http.getData(123)

装饰器的执行顺序

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

如果有多个同样的装饰器,会先执行后面的

function logClass1(params:string){
    return function(target:any){
    }	
}
function logClass2(params:string){
    return function(target:any){
    }	
}
// 先执行装饰器工厂logClass1 logClass2,从上到下执行
// 再执行装饰器工厂返回的装饰器logClass2 logClass1,从下到上执行
@logClass1('hello')
@logClass2('hello')
class HttpClient{
    constructor(){
    }
    getData(){
    }
}