08、TypeScript 装饰器

1.类装饰器

/*
  装饰器:装饰器是一种特殊类型的声明,它能够被附加到类声明、方法、属性或参数上,可以修改类的行为
  通俗的讲装饰器就是一个方法,可以注入到类、方法、属性参数上来扩展类、属性、方法、参数的功能
  常见的装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器
  装饰器的写法:普通装饰器(无法传参)、装饰器工厂(可以传参)
  装饰器是过去几年中 js 最大的成就之一,也是 es7 的标准特性之一
*/
// 类装饰器:类装饰器在类声明之前被声明(紧靠着类声明)。
// 类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。传入一个参数

// 定义一个装饰器(普通装饰器)
function logClass(params: any) {
  // params 就是指当前类
  console.log(params);
  // 给这个类扩展一个属性 url
  params.prototype.url = '动态扩展的属性';
}

// 声明一个类
// 使用装饰器(普通装饰器),可以扩展类的属性和方法,在不修改类的前提下,扩展类的功能
@logClass
class HttpClient {
  name: string;

  constructor(name: string) {
    this.name = name;
  }

  getData(): void {

  }
}

// 实例化,这里加上 any ,h.url 就不会报错了
var h: any = new HttpClient('zhangning');
console.log(h.url);

// 类装饰器工厂(可以传参)
function logClass1(params: string) {// params 表示传递的参数
  return function(target: any) {// 这里的参数 target 指的是当前的类
    console.log(params);// hellow
    console.log(target);// HttpClient(){}
    target.prototype.url = params;
  };
}

@logClass1('hellow')
class HttpClient1 {
  constructor() {
  }

  getData() {

  }
}

let h1: any = new HttpClient1();
console.log(h1.url);

 

  重载构造函数例子

// 重载构造函数的例子,
// 类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。
// 如果类装饰器返回一个值,他会使用提供的构造函数来替换类的声明

// 定义装饰器,在装饰器中修改构造函数(就是重载构造函数)
function logClass11(target: any) {
  console.log(target);
  return class extends target {
    url: any = '我是装饰器中修改的数据';
    getData(){
      console.log(this.url)
    }
  };
}

// 类
@logClass11
class HttpClient11 {
  url: string | undefined;

  constructor() {
    this.url = '我是构造函数中的url';
  }

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

let h11 = new HttpClient11();
h11.getData();// 我是构造函数中的url

 

2.属性装饰器

/*
属性装饰器表达式会在运行时当作函数被调用,传入下列 2 个参数:
  1.对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
  2.成员的名字
*/

// 定义类装饰器
function logClass(params: any) {
  return function(target: any) {
    console.log(target);
    console.log(params);
  };
}

// 定义属性装饰器
function logProperty(params: any) {
  // target:类的原型对象,attr:属性的名称,这里的 attr 就是 url
  return function(target: any, attr: any) {
    console.log(target);
    console.log(attr);
    // target 相当于 类装饰器中的 target.prototype
    target[attr] = params;// 这里就把 HttpClient 中的 url 改成了 '属性装饰器传递的数据'
  };
}

@logClass('xxxx')
class HttpClient {
  @logProperty('属性装饰器传递的数据')
  public url: any | undefined;

  constructor() {
  }

  getData() {

  }
}

 

3.方法装饰器

/*
  方法装饰器:它会被应用到方法的 属性描述符上,可以用来监视,修改或者替换方法定义
  方法装饰会在运行时传入下列三个参数:
    1.对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
    2.成员的名字
    3.成员的属性描述符
*/


// 定义方法装饰器
function logFun(params: any) {
  // 1.对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
  // 2.成员的名字
  // 3.成员的属性描述符
  return function(target: any, methodName: any, desc: any) {
    console.log(target);
    console.log(methodName);
    console.log(desc);
    // 可以扩展属性
    target.name = 'zhangning';
    target.run = function() {
      console.log('跑步');
    };
    // 修改装饰器的方法 把装饰器方法里面传入的所有参数改为 string 类型
    // 1.保存当前的方法
    let oMethos = desc.value;
    desc.value = function(...args: any[]) {
      args = args.map(value => {
        return String(value);
      });
      console.log(args);// args 就是实例传递过来的数组
      oMethos.apply(this, args);
    };
  };
}

class HttpClient {
  public url: any | undefined;

  constructor() {
  }

  @logFun('类装饰器传递参数')
  getData(...args: Array<any>): void {
    console.log(args);// args 就是实例传过来的数组
    console.log(this.url);
  }
}

 

4.方法参数装饰器

/*
  参数装饰器:
    参数装饰器表达式会在运行时当作函数被调用,可以使用参数装饰器为类的原型增加一些元素数据,传入下列三个参数:
    1.对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
    2.方法的名字
    3.参数在函数参数列表中的索引
*/

function logProp(params: any) {
  return function(target: any, methodName: any, paramsIndex: any) {
    console.log(params);
    console.log(target);
    console.log(methodName);
    console.log(paramsIndex);
    target.name = params;
  };
}

class HttpClient {
  public url: any | undefined;

  constructor() {
  }

  getData(@logProp('uuid')uuid: any): void {
    console.log(uuid);
  }
}

let hc: any = new HttpClient();
hc.getData(123);
console.log(hc.name);

 

5.装饰器的执行顺序

// 装饰器的执行顺序
function logClass1(params: string) {
  return function(target: any) {
    console.log('类装饰器1');
  };
}

function logClass2(params: string) {
  return function(target: any) {
    console.log('类装饰器2');
  };
}

function logAttribute(params?: string) {
  return function(target: any, attrname: any) {
    console.log('属性装饰器');
  };
}

function logMethod(params?: string) {
  return function(target: any, attrname: any, desc: any) {
    console.log('方法装饰器');
  };
}

function logparams1(params?: string) {
  return function(target: any, attrname: any, desc: any) {
    console.log('方法参数装饰器1');
  };
}

function logparams2(params?: string) {
  return function(target: any, attrname: any, desc: any) {
    console.log('方法参数装饰器2');
  };
}

// 4.最后执行类装饰器,先执行下面的,再执行上面的
@logClass1('zhangning')
@logClass2('xxx')
class HttpClient {
  // 1.首先执行属性装饰器
  @logAttribute()
  public url: string | undefined;

  constructor() {
  }

  // 2.其次执行方法装饰器
  @logMethod()
  getData() {
    return true;
  }

  // 3.接着执行方法参数装饰器,执行顺序是从后往前
  setData(@logparams1() attr1: any, @logparams2() attr2: any) {

  }
}

let hc: any = new HttpClient();

/*
打印结果:
  属性装饰器
  方法装饰器
  方法参数装饰器2
  方法参数装饰器1
  类装饰器2
  类装饰器1
*/

 

上一篇:[Angular] The $any() type cast function


下一篇:【TS】对已有的模块导出新类型