TypeScript 迷迷糊糊装饰器 / 类、属性、方法、方法参数装饰器 / 装饰器执行顺序

装饰器:一种特殊类型的声明,实际是一个方法,可以注入到了类、属性、方法、参数上来动态扩展类、属性、方法、参数的功能。

装饰器写法

  • 普通装饰器,无法传参,在类的上一行使用@装饰器名字
  • 装饰器工厂,可传参,在类的上一行使用@装饰器名字(装饰器参数)

使用普通装饰器

function logClass(param:any){
  console.log(param);  //param就是当前类,此处输出 Animal类
}

@logClass
class Animal{
  constructor(){

  }
}

给类扩展属性和方法:

function logClass(param:any){
  param.prototype.name = 'xxx'
  param.prototype.eat = function(){
    console.log('eat...');
  }
}

@logClass
class Animal{
  constructor(){

  }
}
let a:any = new Animal()
console.log(a.name);   //xxx
a.eat()   //eat...

上面的装饰器无法传参,下面使用可以传参的装饰器工厂

function logClass(param:any){    //param为装饰器传参
  return function(target:any){   //target为调用装饰器的类
    target.prototype.name = param
  }
}

@logClass('wangwang')
class Animal{
  constructor(){

  }
}
let a:any = new Animal()
console.log(a.name);   //wangwang

@logClass('web')
class Work{
  constructor(){

  }
}
let w:any = new Work()
console.log(w.name);  //web

装饰器

类装饰器

先写一个基本的类

class Animal{
  name:string;
  constructor(){
    this.name = 'Animal构造函数中的name'
  }
}
let a:any = new Animal()
console.log(a.name);   //Animal构造函数中的name

使用类装饰器重写类的构造函数和方法:

function logClass(target:any){   
  return class extends target{
    name:any = '装饰器修改的name'
    eat(){
      console.log('xxxx');
    }
  }
}

@logClass
class Animal{
  name:string;
  constructor(){
    this.name = 'Animal构造函数中的name'
  }
  eat(){
    console.log('eat...');
  }
}
let a:any = new Animal()
console.log(a.name);   //装饰器修改的name
a.eat()   //xxxx

属性装饰器

属性装饰器表达式会在运行时被当作函数调用,传两个参数

function logProperty(param:any){
  return function(target:any,attr:any){
    console.log(target);
    console.log(attr);  
    target[attr] = param   //这里的target相当于类装饰器里的target.prototype
  }
}

class Animal{
  @logProperty('hello')    //这个属性装饰器装饰当前name属性
  name:string|undefined;
  constructor(){

  }
}
let a:any = new Animal()
console.log(a.name);   //hello

方法装饰器

应用在方法的属性描述上,可以用来监视、修改、替换方法定义
三个参数:

  • 对于静态成员来说是类的构造函数,对于实例成员来说是类的原型对象
  • 成员的名字
  • 成员的属性描述符
function log(param:any){
  return function(target:any,name:any,desc:any){
    console.log(target); // HTTPClient类的原型对象
    console.log(name);   // 装饰方法的名字
    console.log(desc);   // 装饰方法的属性描述符
  }
}
class HTTPClient{
  private url:string|undefined
  @log('www.baidu.com')
  getData(){
    console.log('getData');
  }
}

替换装饰的方法:

function log(param:any){
  return function(target:any,name:any,desc:any){
    desc.value = function(...args:any[]){
      args = args.map((v:any)=>{
        return String(v)
      })
      console.log(args);
    }
  }
}
class HTTPClient{
  private url:string|undefined
  @log('www.baidu.com')
  getData(...args:any[]){
    console.log('getData');
  }
}

let http = new HTTPClient()
http.getData('1','2')    // [ '1', '2' ]

上面替换了 HTTPClient 类中的 getData 方法,而不是修改它。可以通过 apply 来修改成员方法,不改变原来的逻辑。

function log(param:any){
  return function(target:any,name:any,desc:any){
    //  1.保存当前方法
    const currentMethod = desc.value
    
    desc.value = function(...args:any[]){
      args = args.map((v:any)=>{
        return String(v)
      })
      console.log(args);
      currentMethod.apply(this,args)
    }
  }
}
class HTTPClient{
  private url:string|undefined
  @log('www.baidu.com')
  getData(...args:any[]){
    console.log('getData');
  }
}

let http = new HTTPClient()
http.getData('1','2')   // [ '1', '2' ]
                        // getData

方法参数装饰器

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

  • 对于静态成员来说是类的构造函数,对于实例成员来说是类的原型对象
  • 方法的名字
  • 参数在函数参数列表中的索引
function log(param:any){
  return function(target:any,name:any,paramsIndex:any){
    console.log(paramsIndex);   // 0
    target.url = param
  }
}
class HTTPClient{
  url:string|undefined
  getData(@log('xxx')value:any){
    console.log(value);
  }
}

let http = new HTTPClient()
http.getData(1234)   // 1234
console.log(http.url);  // xxx

简单记下,不知道这个装饰器的具体有啥特殊用处。

装饰器执行顺序

装饰器的执行顺序为:属性=>方法参数=>方法=>类
其中,
类装饰器、方法参数装饰器的执行顺序是:从后往前
属性装饰器、方法装饰器的执行顺序是:从上至下

function logClass1(param?:any){
  return function(target:any){
    console.log('类装饰器 1');
  }
}
function logClass2(param?:any){
  return function(target:any){
    console.log('类装饰器 2');
  }
}
function logAttr1(param?:any){
  return function(target:any,attr:any){
    console.log('属性装饰器 1');
  }
}
function logAttr2(param?:any){
  return function(target:any,attr:any){
    console.log('属性装饰器 2');
  }
}
function logMethod1(param?:any){
  return function(target:any,name:any,desc:any){
    console.log('方法装饰器 1');
  }
}
function logMethod2(param?:any){
  return function(target:any,name:any,desc:any){
    console.log('方法装饰器 2');
  }
}
function logParams1(param?:any){
  return function(target:any,name:any,index:any){
    console.log('方法参数装饰器 1');
  }
}

function logParams2(param?:any){
  return function(target:any,name:any,index:any){
    console.log('方法参数装饰器 2');
  }
}

@logClass1()
@logClass2()
class HTTPClient{
  @logAttr1()
  url:string|undefined
  @logAttr2()
  name:string|undefined
  @logMethod1()
  setData(){ }

  @logMethod2()
  getData(@logParams1()value:any,@logParams2()type:any) {
    console.log(value);
  }
}

输出结果为:

// 属性装饰器 1
// 属性装饰器 2
// 方法装饰器 1
// 方法参数装饰器 2
// 方法参数装饰器 1
// 方法装饰器 2
// 类装饰器 2
// 类装饰器 1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值