美文网首页
typeScript--再学装饰器

typeScript--再学装饰器

作者: 习惯水文的前端苏 | 来源:发表于2023-04-03 20:27 被阅读0次

    好文推荐

    localStorage的别样用法
    借助npm包统一包管理器

    前言

    约是两年前,就对着ts文档学习了装饰器的使用,我是相信温故而知新的,故今天重新在学一遍,相信会有不一样的收获

    启用

    在项目中,需要在tsconfig.json中显示的启用

    {
      "compilerOptions": {
        "experimentalDecorators": true
      }
    }
    

    装饰器分类

    1-类装饰器
    2-属性装饰器
    3-方法装饰器
    4-参数装饰器
    5-访问器装饰器

    执行顺序

    • 参数装饰器,然后依次是方法装饰器,访问符装饰器,或属性装饰器应用到每个实例成员
    • 参数装饰器,然后依次是方法装饰器,访问符装饰器,或属性装饰器应用到每个静态成员
    • 参数装饰器应用到构造函数。
    • 类装饰器应用到类。

    使用

    • 类装饰器

    1-其ts定义如下

    可以看到,类的本质是Function类型,这意味着,我们可以通过prototype来进行扩展,同时从返回值可以猜测,如果返回值类型不是void,则极大可能性会将返回的值作为装饰器本身

    declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;
    

    2-返回void

    function sex<T extends {new(...args:any[]):{}}>(constructor:T){
      constructor.prototype.logSex = function(){
        console.log('男')
      }
    }
    
    @sex
    class Person{
    }
    
    (new Person() as any).logSex()
    

    3-返回Function

    上一个返回void的示例是无法传递参数的,可以通过返回函数类型来解决

    function sex(msg:string){
      return function<T extends {new(...args:any[]):{}}>(constructor:T){
        constructor.prototype.logSex = function(){
          console.log(msg)
        }
      }
    }
    
    @sex('用户的')
    class Person{
    }
    (new Person() as any).logSex()
    

    4-重载

    由于class本质上也是Function类型,所以这可以看作是3的另一种表达形式,且更灵活些

    function sex<T extends {new(...args:any[]):{}}>(Tar:T){
      return class extends Tar{
        constructor(...args:any[]){
          super()
        }
      }
    }
    
    @sex
    class Person{
    }
    
    • 属性装饰器

    1-定义

    从定义可知,其也是一个函数,且被执行时会接收两个参数

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

    declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void;
    

    2-使用

    我们可以对某个属性进行监控,并当某个操作行为发生时执行一些关联动作

    function reactive(Tar:any,p:string){
      let value = Tar[p]
    
      const get = function(){
        return value
      }
    
      const set = function(newValue:any){
        Tar.changeName(newValue)
        value = newValue
      }
    
      if(Reflect.deleteProperty(Tar,p)){
        Object.defineProperty(Tar,p,{
          set,
          get,
          enumerable:true,
          configurable:true
        })
      }
    
    }
    
    class Person{
      @reactive
      name:string=''
      changeName(newName:string){
        console.log('改名字了,新名字是:'+newName)
      }
    }
    const p = new Person() 
    p.name
    p.name = '23'
    console.log(p.name)
    
    • 方法装饰器

    1-定义

    declare type MethodDecorator = 
    <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) 
    => TypedPropertyDescriptor<T> | void;
    

    相比较属性选择器,其多了参数三,其对应的ts定义如下

    value属性表示的是函数体,这意味着可以先将原函数体缓存后执行重写等操作

    interface TypedPropertyDescriptor<T> {
        enumerable?: boolean;
        configurable?: boolean;
        writable?: boolean;
        value?: T;
        get?: () => T;
        set?: (value: T) => void;
    }
    

    2-示例

    拿我之前写npm包时遇到的一个需求举例,在初始化的时候用到了异步数据,在异步执行结束前如果用户执行了do方法,则需要对其进行缓存,等异步结束后再取回依次调用,伪代码如下

    import { config } from '.'
    
    function delay(_,_, descriptor){
      const value = descriptor.value
      descriptor.value = function(...args:any[]){
        if(config.asyning){
            config.que.push(descriptor)
        }else{
          value.apply(this,args)
          config.que = []
        }
      }
      return descriptor
    }
    
    class NAS{
      @delay
      do(){
      }
    }
    
    • 参数装饰器

    1-定义

    declare type ParameterDecorator = 
    (target: Object, propertyKey: string | symbol, parameterIndex: number) => void;
    

    2-示例

    在参数装饰器执行时收集必选信息,当运行方法装饰器时重写descriptor.value并加入判断逻辑

    const requiredMap:any = {
    
    }
    function required(_: any, propertyKey: string, parameterIndex: number){
      requiredMap[propertyKey]=parameterIndex
    }
    
    function validate(_:any,__:any, descriptor:any){
      const i = requiredMap[descriptor.value.name]
      if(i !== undefined){
        descriptor.value = (...args:any[])=>{
          if(args[i] === undefined){
            throw new Error('参数缺失')
          }
        }
      }
      return descriptor
    }
    
    class Person{
      @validate
      changeName(@required newName:string){
        console.log('改名字了,新名字是:'+newName)
      }
    }
    const p = new Person() 
    p.changeName()
    
    • 访问器装饰器

    看文档吧,感觉没啥可说的,主要没实际用过,后续有应用场景了补充

    相关文章

      网友评论

          本文标题:typeScript--再学装饰器

          本文链接:https://www.haomeiwen.com/subject/vlmdddtx.html