typescript(ts)面向对象之装饰器

简介: 每一个属性都要验证,我们就要写一个if 来进行判断,100个属性就要写100遍,然后每一个if中的条件还有可能相同,就会导致我们写重复的代码,这是非常麻烦的。

装饰器概念


装饰器是面向对象的概念(在java中叫做注解,早c#中叫做特征,英文名字是decorator

在angular大量使用,react中也会用到

JS支持装饰器,目前处于建议征集的第二阶段,翻阅网上的大量文章,发现17年的时候就开始有装饰器了,也一直都在意见征集阶段,直到现在也是一样的。ts 中需要使用这个需要在 配置文件ts.config.json 中开启 experimentalDecorators: true就能使用装饰器了。


20210202110934491.png


装饰器的作用


看下面的例子中:


class A {
  constructor(
   private prop1: string,
   private prop2: number,
   private prop3: boolean,
   private prop4: string,
   private prop5: number,
   private prop6: boolean,
   // ... 这里还有有100个属性
  ){}
}


我们现在要做一个事情,对上面的每一个属性进行验证。按照现在的ts 的方式,我们可以这么做:


class A {
  constructor(
   private prop1: string,
   private prop2: number,
   private prop3: boolean,
   private prop4: string,
   private prop5: number,
   private prop6: boolean,
   // ... 这里还有有100个属性
  ){}
  /**
  *验证函数
  */
  public validate(){
    if(!this.prop1 && this.prop1.length < 10){
      // 验证prop1
    }
    if (this.prop2 > 0) {
    }
    if (!this.prop3) {
    }
    // ...
  }
}


看上面的验证代码,我们可以发现一些不合理的地方,


  • 每一个属性都要验证,我们就要写一个if 来进行判断,100个属性就要写100遍,然后每一个if中的条件还有可能相同,就会导致我们写重复的代码,这是非常麻烦的。
  • 我们在写一个类的属性的时候,才是最清楚这个成员的条条框框,过了一会儿在写,可能都不记得。换一句话说是,类的成员与成员验证分开是不易于代码的维护的。


解决的问题


装饰器,能够带来额外的信息量,可以达到分离关注点的目的。


  • 信息书写位置的问题
  • 重复代码的问题


上述两个问题产生的根源:某些信息,在定义时,能够附加的信息量有限。


装饰器的作用:为某些属性、类、参数、方法提供元数据信息(metadata)


元数据:描述数据的数据


基于上面的这些因素,js 就提出了 装饰器的概念(现在依旧还没有正式发布),有了装饰器后,让前端的js 类的代码更像java 等后端语言了。但是我们要清楚的是,装饰器是js 中提出的,并不是ts 中的特殊语法。


原理本质


本质


在JS中,装饰器是一个函数。(装饰器是要参与运行的)


装饰器可以修饰:


  • 成员(属性+方法)
  • 参数


使用方法:


使用装饰器@装饰器函数


/**
 * 不为空判断
 * @param target 
 * @param prop 
 */
function NotNull(target: any, prop: string) {
  // 判断静态属性
 if (target.prototype && !target[prop]) {
   console.log(`${prop} 不能为空`)
 }else{
   // 判断实例属性,这个需要使用工厂函数来实现动态创建类
   if (!new (A as any)()[prop]) {
    console.log(`${prop} 不能为空`)
   }
 }
}
class A {
  @NotNull
  private prop1: string = ''
  @NotNull
  private prop2: number = 1;
  @NotNull
  static prop3: boolean = false;
}


效果:


20210202155041716.png


原理


上面的代码我们可以使用es5的代码来写出来进行分析:


function A(){}
A.prototype.prop1 = '';
__decorator([NotNull],A.prototype,'prop1');
A.prototype.prop2 = 1;
// 这里传入的A.prototype稍后解释
__decorator([NotNull],A.prototype,'prop2');
// 静态属性
A.prop3 = false;
// 这里传入的A 稍后解释
__decorator([NotNull],A,'prop3 ');
/**
*不为空的函数
*/
function NotNull(target, prop){
if(!target[prop]){
   console.log(`${prop} 这个属性不能为空`)
  }
}
/**
*装饰者函数, 这个是自己根据网上的文档进行理解出来的。
*/
function __decorator (decorators, target, prop){
  for(var i = 0; i < decorators.length; i ++){
     decorators[i](target, prop)
  }
}


效果:


20210202155150705.png


通过上面的代码简写,我们可以得出以下结论(装饰器在ts中的前提)


  • 装饰器的执行时间是在 属性 定义好后就直接执行的
  • 装饰器就是一个函数,在ts 中会生成额外的代码
  • 装饰器可以做到面向切面编程(AOP),上面的代码不影响,是直接在定义就完成了验证。


装饰器的使用方式


类中使用装饰器


类装饰器的本质是一个函数,该函数接收一个参数(必须),表示类本身(构造函数本身)


使用装饰器@得到一个函数


在TS中,如何约束一个变量为类


  • Function
  • new (参数)=>object


装饰器函数的运行时间:在类定义后直接运行


类装饰器可以具有的返回值:


  • void:仅运行函数


20210202163426739.png


  • 返回一个新的类:会将新的类替换掉装饰目标


20210202163739973.png


  • 也可以在里面实现伪继承,但是不建议这么做(下面的列子是装饰器工厂)


2021020216532796.png


  • 多个装饰器的情况:会按照后加入先调用的顺序进行调用。


20210202165812789.png

20210202170155661.png


成员装饰器


  • 属性


属性装饰器也是一个函数,该函数需要两个参数:


1.如果是静态属性,则为类本身;如果是实例属性,则为类的原型;

2.固定为一个字符串,表示属性名


20210202171600761.png


注意: 这里的测试需要ts 编译的结果是es6以上,不然A和A的原型都是undefined,得到的结果会是错误的。换一句话说,需要将 ts.config.json 的 target: 'es6' ,因为es5是没有类的,打印A和A的原型会报错哦


  • 方法


方法装饰器也是一个函数,该函数需要三个参数:


1.如果是静态方法,则为类本身;如果是实例方法,则为类的原型;

2.固定为一个字符串,表示方法名

3.属性描述对象


20210202172442643.png


成员也可以可以有多个装饰器修饰,和类装饰器使用的方式一样,也可以使用装饰器工厂


20210202172859632.png


参数中使用装饰器


要求函数有三个参数:


1.如果方法是静态的,则为类本身;如果方法是实例方法,则为类的原型

2.方法名称或者undefined

3.在参数列表中的索引


20210202174656849.png

20210202174806410.png


使用第三方库


元数据基础库 reflect-metadata


npm install reflect-metadata -S 这个库是用于保存元数据的初始值,这个是需要安装在生成环境的,因为装饰器是需要在代码运行时候起作用的。在 ts.config.json中加入配置emitDecoratorMetadata: true就会在编译结果中加入元数据。则TS在编译结果中,会将约束的类型,作为元数据加入到相应位置。这样一来,TS的类型检查(约束)将有机会在运行时进行。


使用方式:

看文档


类的验证库class-validator


npm install class-validator --save 这个库是对类进行验证的,使用方式,

看文档


把普通对象转类 class-transformer


npm install class-transformer --save 这个库是用于把一维的对象转成类,使用方式

看文档


上面的这两个库都需要依赖于 reflect-metadata (元数据的基础库)

相关文章
|
6月前
|
JavaScript 前端开发 数据安全/隐私保护
TypeScript中装饰器的概念与使用场景
【4月更文挑战第23天】TypeScript的装饰器是特殊声明,用于附加到类的声明、方法、属性或参数,以修改行为。它们以`@expression`形式,其中`expression`是运行时调用的函数。装饰器应用场景包括:日志记录、调试、依赖注入、权限控制和AOP。通过装饰器,可以实现动态行为修改,如添加日志、注入依赖、控制权限以及事务管理。然而,应谨慎使用,避免过度复杂化代码。装饰器在现代 TypeScript 开发中扮演重要角色,帮助编写更健壮、可维护的代码。
|
6月前
|
存储 JavaScript 前端开发
TS中 说说数组在TypeScript中是如何工作的?
TS中 说说数组在TypeScript中是如何工作的?
|
6月前
|
JavaScript
【typeScript】搭建TS环境
【typeScript】搭建TS环境
|
资源调度 JavaScript 前端开发
【TypeScript】TS 看这一篇就够了
【TypeScript】TS 看这一篇就够了
531 0
|
6月前
|
存储 JavaScript 前端开发
TypeScript 5.2 beta 浅析:新的关键字 using 与新版装饰器元数据
TypeScript 5.2 beta 浅析:新的关键字 using 与新版装饰器元数据
109 0
|
2月前
|
JavaScript
typeScript基础(3)_ts函数默认值和可选参数
本文介绍了在TypeScript中如何使用函数的默认值和可选参数。展示了如何为函数参数指定默认值,使得在调用函数时可以省略某些参数,以及如何定义可选参数。
154 2
|
1月前
|
JavaScript 索引
TypeScript(TS)安装指南与基础教程学习全攻略(二)
TypeScript(TS)安装指南与基础教程学习全攻略(二)
52 0
|
1月前
|
JavaScript 前端开发 安全
TypeScript(TS)安装指南与基础教程学习全攻略(一)
TypeScript(TS)安装指南与基础教程学习全攻略(一)
28 0
|
2月前
|
JavaScript 前端开发
typeScript基础(8)_ts类型断言
本文介绍了TypeScript中的类型断言,它用于在编译时告诉TypeScript某个对象具有特定的类型,即使它看起来不具备。类型断言可以用来访问一个类型上存在而另一个类型上不存在的属性或方法。需要注意的是,类型断言并不会在运行时改变JavaScript的行为,因此如果断言不当,运行时仍然可能出错。文章还提醒避免将类型断言为`any`类型或进行多重断言。
34 1
|
4月前
|
JavaScript 前端开发 索引
TypeScript(八)装饰器
TypeScript(八)装饰器
31 0