• TypeScript 的装饰器有哪些?


    大家好,我是前端西瓜哥。

    JS 的装饰器还在提案中(提案了好久),还没进入正式标准,掌握半成品实在性价比不高。

    但装饰器实在是太强了,TypeScript 还是基于第一版实现了自己的装饰器特性,并标明为实验性质,让大家能够早早地用上。

    目前也不少知名的第三方库(比如 Nest.js)使用了 TS 的装饰器,还是有必要学习的。

    但是呢,TS 的装饰器实现已经和 ECMAScript 的装饰器提案越走越远。

    但因为 TS 装饰器被不少知名的第三方库使用,我们可能还是得使用和标准不同的装饰器。

    以后两种装饰器的实现就要打架了,实在是太乱了,库作者大概要吐了。

    TS 中实现的装饰器有:

    1. 类装饰器

    2. 方法装饰器

    3. 访问器装饰器

    4. 属性装饰器

    5. 参数装饰器

    在使用类装饰器前,你需要在 tsconfig.json 中启用实验性的装饰器配置:

    {
      "compilerOptions": {
        // ...
        "experimentalDecorators": true
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    装饰器其实是一个函数,需要在被类使用前声明。因为 类声明后立刻就会执行修饰器,所以如果没有提前声明,就会报错。

    类装饰器

    类装饰器是一个函数,它可以在 class 声明时拿到 class,然后对 class 进行一些操作。

    给一个类应用类装饰器的方式是:在类名的上一行加上 @<装饰器名>

    // 类装饰器
    function changeDefaultPrice(constructor: Function & {defaultPrice: number}) {
      constructor.defaultPrice = 100;
    }
    
    @changeDefaultPrice
    class Watermelon {
      static defaultPrice = 9;
    }
    
    Watermelon.defaultPrice // 100
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    可以看到上面代码中,Watermelon 类有个静态属性 defaultPrice,值为 9,表示默认西瓜只要 9 块钱。

    太便宜了,于是我实现了个 changeDefaultPrice 装饰器,能够从函数参数中拿到类,并将其修改为 100 块。

    有时候,我们希望可以修改为自定义价格。这时候我们可以使用 装饰器工厂函数

    所谓装饰器工厂函数,就是一个返回装饰器函数的函数。通过它,我们就能利用闭包注入变量。

    // 类装饰器工厂函数
    function changeDefaultPrice(price = 100) {
      return function (constructor: Function & { defaultPrice: number }) {
        constructor.defaultPrice = price;
      };
    }
    
    // 这里可自定义默认价格
    @changeDefaultPrice(50)
    class Watermelon {
      static defaultPrice = 9;
    }
    
    Watermelon.defaultPrice // 50
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    装饰器工厂函数不只可以用在类装饰器中,还可以用在其他类型的装饰器中。

    方法装饰器

    方法装饰器可以接受的参数有:

    • target:类或者类的原型对象,下面的代码拿到的就是 Watermelon.prototype。如果是给 static 标识的方法加装饰,拿到的就是类;

    • prop:方法名,下面代码拿到的是 "say" 字符串;

    • descriptor:该 prop 的描述符,我们可以直接修改这个对象,然后这个对象会被装饰器重新应用到这个属性上。

    // 方法装饰器
    function changeMethod(
      target: any,
      prop: string | symbol,
      descriptor: PropertyDescriptor
    ) {
      // 重写方法
      descriptor.value = function () {
        console.log("我没裂开");
      };
    }
    
    class Watermelon {
      @changeMethod
      say() {
        console.log("我裂开了");
      }
    }
    
    const wm = new Watermelon();
    wm.say(); // 我没裂开
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    上面代码用 changeMethod 方法装饰器重写了 Watermelon.prototype.say 方法。

    如果 TS 编译没能通过,你需要再给 tsconfig.json 配置上 target,且使其大于等于 ES5 版本。默认的 ES3 版本有些 API 都不支持

    访问器装饰器

    访问器装饰器,对类的 get 或 set 方法进行装饰。

    和方法装饰器类似,访问器装饰器获得的参数有:

    1. target:类或类的原型对象;

    2. prop:成员名;

    3. descriptor:成员的描述符。

    function doublePrice(
     target: Object,
      prop: string | symbol,
      descriptor: PropertyDescriptor
    ) {
      const getter = descriptor.get!;
      descriptor.get = function() {
        return getter.call(this) * 2;
      }
    }
    
    class Watermelon {
      private _price = 9;
    
      @doublePrice
      get price() {
        return this._price;
      }
    }
    
    const wm = new Watermelon();
    wm.price // 18
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    上面代码将 price 的值翻倍了。

    属性装饰器

    属性装饰器,顾名思义用于修饰类的属性。

    属性修饰器接受的参数有:

    1. taget:类或类的原型对象;

    2. prop:属性名。

    function propDeco(target: any, prop: string | symbol) {
      console.log({ target, prop });
    }
    
    class Watermelon {
      @propDeco
      goodName = "西瓜";
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    参数装饰器

    参数装饰器用于装饰函数参数,用于类构造器和方法。

    参数装饰器能获得的参数有:

    1. target:类或类的原型对象

    2. prop:函数名,或 undefined(函数为构造函数时)

    3. paramIdx:被装饰的参数的位置。

    function validatePhone(target: Object, prop: string | symbol, paramIdx: number) {
      //
    }
    
    class Watermelon {
      constructor(a: string, @validatePhone b: string) {
        console.log(a, b);
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    参数装饰器的一个用途是可以做参数校验,将某个索引的位置保存起来,然后再利用方法装饰器拿到描述符 descriptor,对原方法做一层校验的封装。

    总结

    总的看来,TS 装饰器可以在类上加一些标记,然后对应的装饰器就能拿到必要的信息(类的原型、方法名、描述符等),然后就可以做一些代理、记录信息的功能增强。

    TS 装饰器最后编译成的 JS 会丢掉这些装饰器标记,本质其实是语法糖。

    我是前端西瓜哥,欢迎关注我,学习更多前端知识。

  • 相关阅读:
    电脑硬件——硬盘
    C语言--冒泡排序和简答选择排序
    如何编写一个Perl爬虫程序
    《大厂高并发分布式锁从入门到实战》第2讲之redis分布式锁
    强化学习从基础到进阶--案例与实践[7]:深度确定性策略梯度DDPG算法、双延迟深度确定性策略梯度TD3算法详解
    Wasserstein Slim GAIN with Gradient Penalty(WSGAIN-GP)介绍及代码实现——基于生成对抗网络的缺失数据填补
    三、虚拟机的迁移和删除
    如何使用Python编写脚本来自动获取和保存网络小说
    git本地仓库及远端仓库推送【linux】
    在vue项目中使用electron
  • 原文地址:https://blog.csdn.net/fe_watermelon/article/details/126918634