TS装饰器

请添加图片描述

通过本文你可以知道什么


  • 装饰器的发展历程
  • JS和TS装饰器有何不同
  • Angular中的装饰器到底是什么
  • 装饰器的定义,语法,作用
  • reflect-meta是什么及如何使用

前言


我们平常开发中或多或少的听说或使用过装饰器,也切身感受到了它带给我们的便利。但是应该很少去系统的了解过装饰器。不清楚装饰器到底擅长干什么,怎么干。
由于目前js和ts中的装饰器有很多不同,本期只聚焦于ts的装饰器进行探讨。
本文预计阅读时间——20分钟

装饰器的演变


  • 2015-3-24
    • stage 1阶段,也是目前广为使用的用法,也基本等同于TS开启了experimentalDecorators的用法。
  • 2018-09
    • 进入到stage2阶段,用法和stage1很大不同
  • 2021-12
    • 针对stage2提案进行了一次修改。
  • 2022-03
    • 正式进入stage3。去掉了metadata部分,使用方式没有发生太大变化。

冷知识:ts只会对Stage-3以上的提案提供支持,而TS引入装饰器实在2015年3月,差不多stage-1的时间段,这是因为在 NG-Conf上,angular团队宣布与TS团队进行合作。

JS装饰器和TS装饰器


js原生目前不支持装饰器,装饰器提案在stage-3阶段,只能通过babel体验装饰器这个新特性。TS目前实现的装饰器是基于JS装饰器stage-1的语法,所以在JS装饰器正式发布后,会和TS装饰器语法产生差异,之后看TS团队如何处理了,但预计也不是近期的事情了。

定义


装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,访问符,属性或参数上。装饰器使用@expression这种形式,expression求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息作为参数传入。
定义来自:https://www.tslang.cn/docs/handbook/decorators.html

配置


由于装饰器目前还是实验中的特定,在js中处于stage-3阶段。在ts中已经作为一项实验性予以支持。开启装饰器需要在tsconfig.json文件中启用 experimentalDecorators 编译器选项。

装饰器于2022年三月底刚进入了stage-3阶段,详情见https://github.com/tc39/proposal-decorators/pull/454

请添加图片描述

Angular中的装饰器


我们在使用angular中经常会看到此类代码
image.png

每个指令,组件,module都会有对应的@expression进行标注,完全吻合装饰器的写法。但其实这种@Component类似的写法不能称作装饰器,更贴切的叫法为注解(Annotation)。它们是用于给编译器做数据描述,最终在build阶段会完全被抹去。
注解并不产生任何行为,仅仅添加附加内容。

装饰器使用


类装饰器

类装饰器是我们最常使用到的,它的通常作用是,为该类扩展功能

  1. 类装饰器有且只有一个参数,参数为类的构造函数constructor
  2. 如果类装饰器返回一个值,它会使用提供的构造函数来替换类的声明

如果你要返回一个新的构造函数,你必须注意处理好原来的原型链。 在运行时的装饰器调用逻辑中不会为你做这些。—— 官方文档

设想有这样一个场景。
目前有一个Tank类,有一个Plane类,有一个Animal类。这三个类都需要一个公共的方法来获取他们所在的位置。我们第一可能想到使用继承来实现。

class BaseClass {
   
    getPosition() {
   
        return {
   
            x: 100,
            y: 200,
            z: 300,
        }
    }
}
class Tank extends BaseClass{
   }
class Plane extends BaseClass {
   }
class Animal extends BaseClass {
   }

这样三个类都可以调用getPosition方法来获取各自的位置了。到目前为止看起来没什么问题。

现在又有了一个新的诉求,Tank 类和Plane类需要一个新的方法addPetrol来给坦克和飞机加油。而动物不需要加油。此时这种写法好像不能继续进行下去了。而js目前没有直接语法提供多继承的功能,我们的继承方式好像行不通了。这时候装饰器可以很完美的实现这样的功能。此时就可以请我们的装饰器闪亮登场了~


装饰器功能之——能力扩展
我们把getPositionaddPertrol都抽象成一个单独的功能,它们得作用是给宿主扩展对应的功能。

const getPositionDecorator: ClassDecorator = (constructor: Function) => {
   
    constructor.prototype.getPosition = () => {
   
        return [100, 200]
    }
}

const addPetrolDecorator: ClassDecorator = (constructor: Function) => {
   
    constructor.prototype.addPetrol = () => {
   
        // do something
        console.log(`${
     constructor.name}进行加油`);
    }
}

@addPetrolDecorator
@getPositionDecorator
class Tank {
   }
@addPetrolDecorator
@getPositionDecorator
class Plane {
   }

@getPositionDecorator
class Animal {
   }

这样的话,加入日后我们有其他的猫猫狗狗,都可以对他进行能力扩展,让其具有加油的能力。

多个装饰器叠加的时候,执行顺序为离被装饰对象越近的装饰器越先执行。

装饰器功能之——重载构造函数
在类装饰器中如果返回一个值,它会使用提供的构造函数来替换类的声明。

function classDecorator<T extends {
     new(...args:any[]):{
     }}>(constructor:T) {
   
    return class extends constructor {
   
        newProperty = "new property";
        hello = "override";
    }
}

@classDecorator
class Greeter {
   
    property = "property";
    hello: string;
    constructor(m: string) {
   
        this.hello = m;
    }
  • 16
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值