ts回顾记录学习二-ts中的装饰器

36 阅读6分钟

ts中的装饰器

装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,访问符,属性或参数上。装饰器使用@expression这种形式,expression必须是一个函数,它会在运行时被调用,被装饰的声明信息作为参数传入。

ts中使用装饰器,必须在tsconfig.json中配置才可以识别使用:启用如下命令: "experimentalDecorators": true

我们根据应用到不同的类型上定义不同的装饰器

  • 添加到类上,类装饰器
  • 添加到方法上,方法装饰器
  • 添加到访问器上,访问器装饰器
  • 添加到属性上,属性装饰器
  • 添加到参数上,参数装饰器

装饰器工厂:如果我们要定制一个装饰器如何应用到一个声明上,我们的得写一个装饰器工厂函数,装饰器工厂就是一个简单的函数,他返回一个表达式,以供装饰器在运行时调用

1.类装饰器

  • 类装饰器就在类声明之前被声明
  • 类装饰器被应用于类的构造函数,可以用来观察,修改或者替换类定义
  • 类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数
  • 如果类装饰器返回一个值,他会使用提供的构造函数来替换类的声明

我们先来是一个简单的类装饰器

//定义装饰器-就是一个函数
function testDecorator(constructor:any){  //类装饰器是用构造函数来作为唯一的参数,我们就传入constructor比较好理解
    // 定义一个属性
    constructor.prototype.name='taotao'
    // 定义一个方法
    constructor.prototype.show=():void=>{
        console.log(`my name is ${constructor.prototype.name}`)
    }
}

@testDecorator  //可以扩展类的属性和方法
class Person{

}
 
let p =new Person()
p.show()   //my name is taotao
console.log(p.name)  //taotao

那么接下来我们提个小的需求,就是我们给构造器可以传入一个布尔值参数,来确定是否可以打印出内容 ,传入true 就可以打印;传入false,不可以打印。这个时候就需要装饰器工厂来帮忙:代码如下

function testDecorator(flag:boolean){
    if(flag){
        return function (constructor:any){  //类装饰器是用构造函数来作为唯一的参数,我们就传入constructor比较好理解
            // 定义一个属性
            constructor.prototype.name='taotao'
            // 定义一个方法
            constructor.prototype.show=():void=>{
                console.log(`my name is ${constructor.prototype.name}`)
            }
        }
    }else{
        return function(constructor:any){}  //不打印任务东西
    }
   
}


@testDecorator(false)  //可以扩展类的属性和方法
class Person{

}
 
let p =new Person()
p.show()
console.log(p.name)

当我们传入的参数是false时,控制台会报如下错误:

Snipaste_2022-11-13_18-00-30.png

当我们传入的参数是true时,能够正确的输出

2.方法装饰器

  • 方法装饰器写在一个方法的声明之前
  • 方法装饰器可以用来监视,修改或者替换方法定义
  • 方法装饰器表达式会在运行时当作函数调用,需要传入下列3个参数:
    1. 静态成员的类的构造函数,或者实例成员类的原型
    2. 成员的名称
    3. 该成员的属性描述符

定义一个方法的装饰器代码如下:

// 定义一个方法装饰器 
// target   普通方法对应的是prototype   静态方法对应的是 类的构造函数
function getNameDecorator(target:any,key:string,desciptor:PropertyDescriptor){
    // 我们打印一下看下这三个参数的具体的表示的内容
  console.log(target)
  console.log(key)   //方法的名字getName
  console.log(desciptor)  //  {  属性描述符
                            //     value: [Function: getName],
                            //     writable: true,
                            //     enumerable: false,
                            //     configurable: true
                            //   }

//   desciptor.writable=false   //这个就是值方法getName不能被修改,如果修改就会报错
  desciptor.value=function(){   //可以自定义方法的返回值和具体的逻辑代码
    return 'new hello'
  }
}

class Test{
    name:string='taotao'
    constructor(name:string){
        this.name=name
    };
    @getNameDecorator
    getName(){
        return this.name
    };
    show():void{
        console.log('hello show')
    }
}

let t=new Test('hahahah')
// console.log(t)
// t.getName=function(){
//     return 'heelo '
// }
console.log(t.getName())  //new hello 

3.访问器装饰器

  • 访问器装饰器声明在一个访问器声明之前(紧靠着访问器声明)。访问器装饰器应用于访问器的属性描述法 并且可以用来监视,修改或者替换一个访问器的定义。访问器装饰器不能用在声明文件中(.d.ts),或者任何外部上下文(比如declare的类)里
  • 访问器装饰器表达式会在运行时当作函数被调用,传入下列3个参数:
    1. 对于静态成员来说是类的构造函数,对于实例来说是类的原型对象
    2. 成员的名字
    3. 描述对象 需要注意的一点是:typescript不允许同时装饰一个成员你的get和set访问器 一个访问器装饰器代码的例子:
// 定义访问器装饰器
function getNameDecorator1(target:any,key:string,desciptor:PropertyDescriptor){
    console.log(target)
    console.log(key)
    console.log(desciptor)

    //  desciptor.writable=false

    // desciptor.value=function(){
    //     return 'new taotao'
    // }
}

class Test1{
    private _name:string
    constructor(name:string){
        this._name=name
    }

    // 对于私有属性的访问器
    @getNameDecorator1
    get name(){
        console.log('get')
        return this._name
    }
    set name(newName){
        console.log('set')
        this._name=newName
    }
}

let t1=new Test1('taotao')
t1.name='hlelo '  
console.log(t1.name)

4.属性装饰器

  • 属性装饰器写在一个属性声明之前(紧靠着属性声明)
  • 属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数:
    1. 对于静态属性来说就是当前的类,对于实例属性来说就是当前实例
    2. 成员的名字 属性装饰器的代码的例子:
function getName(target:any,key:string):any{
//   自己第三个属性让属性不能被修改
//   const descriptor:PropertyDescriptor={
//     writable:false
//   }
//   return descriptor  //加上之后就不能修改属性值

target[key]='yuxuan' //原型链上的name属性
}

class Test{
   @getName
   name='taotao'
}

let t=new Test()
// t.name='coco'  //此时能被修改
console.log(t.name)
console.log((t as any).__proto__.name)  //yuxuan

5.参数装饰器

  • 参数装饰器声明在一个参数声明之前(紧靠着参数声明)
  • 参数装饰器应用于类构造函数或方法声明
  • 参数装饰器不能用在声明文件中(.d.ts),重载或者任何外部上下文(比如declare的类)里
  • 参数装饰器表达式会在运行时当作函数被调用,传入下列3个参数
  1. 对于静态成员来说是类的构造函数,对于实例来说是类的原型对象
  2. 参数所在的方法的名字
  3. 参数在参数列表的索引
function getName(target:any,key:string,index:number){
    console.log(target)
    console.log(key)
    console.log(index)
}

class Test{
    getInfo(@getName name:string,age:number){
      console.log(name,age)
    }
}

let t=new Test()
t.getInfo('taotao',28)

6.装饰器的小例子:try....catch...利用装饰器封装

const userInfo:any=undefined

class  Test{
    getName(){
        return userInfo.name
    }
    getAge(){
        return userInfo.age
    }
}

let t=new Test()
t.getName()  //会报userInfo不存在name属性  我们使用try...catch来增加代码的兼容性
t.getAge()

加上try....catch...

const userInfo:any=undefined

class  Test{
    getName(){
        try{
            return userInfo.name
        }catch(e){
           console.log(e)
        }
     
    }
    getAge(){
        try{
            return userInfo.age
        }catch(e){
           console.log(e)
        }
    }
}

let t=new Test()
t.getName()
t.getAge()

虽然加上了try...catch...,但是我们如果需要加的地方比较多呢,我们用方法装饰器来实现

const userInfo:any=undefined

//定义方法装饰器
const getErrorDecorator=function (target:any,key:string,descriptor:PropertyDescriptor){
    const fn=descriptor.value
    descriptor.value=function(){
        try{
             fn()
        }catch(e){
          console.log('userinfo上不存在该属性')
        }
    }
}

class  Test{
    @getErrorDecorator  //使用方法装饰器
    getName(){
            return userInfo.name
    }
    @getErrorDecorator  //使用方法装饰器
    getAge(){
            return userInfo.age
    }
}

let t=new Test()
t.getName()  //userinfo上不存在该属性
t.getAge()   //userinfo上不存在该属性

但是我们如果想打印不同的错误信息,该如何操作呢?需要装饰器工厂来帮忙

const userInfo:any=undefined

//定义装饰器工厂,返回一个装饰器
function getErrorDecorator(msg:string){
  return  function (target:any,key:string,descriptor:PropertyDescriptor){
    const fn=descriptor.value
    descriptor.value=function(){
        try{
             fn()
        }catch(e){
           console.log(msg)
        }
    }
}
}


class  Test{
    @getErrorDecorator('userinfo上不存在name属性')
    getName(){
            return userInfo.name
    }
    @getErrorDecorator('userinfo上不存在age属性')
    getAge(){
            return userInfo.age
    }
}

let t=new Test()
t.getName()  //userinfo上不存在name属性
t.getAge()   //userinfo上不存在age属性