代码缺乏装饰?使用ts装饰器来装饰你的代码

简介: 该文章详细介绍了TypeScript中的装饰器特性,包括类装饰器、方法装饰器、属性装饰器和参数装饰器的定义与使用方法,并通过具体示例展示了装饰器在实际编码中的应用技巧。

👏序言

ts 中,有一个经常被我们熟用但是又很少去注意的一个知识点,装饰器。那在下文中,将讲解类的装饰器,一起类装饰器中的几种其他的装饰器。

下面开始本文的讲解~😜

😉一、类的装饰器

1、什么是装饰器

首先,我们先来讲, TypeScript 中,类的装饰器是什么。

装饰器实际上是一种对类的修饰工具。比如说:某一天可能有个女孩子想要出去逛街,那么她可能会画个美美的妆出门。因此,我们可以把装饰器视为是化妆的这个过程,也就是一个美化的过程。

现在就是,假设我们有一个类,然后呢,要对它额外进行一些修饰,这个就是装饰器要干的事情了。

2、装饰器的特点

首先我们需要先来了解装饰器的几个特点。具体如下:

  • 装饰器本身就是一个函数
  • 装饰器接收的参数是构造函数
  • 装饰器通过 @ 符号来进行使用。

依据以上这几个特点,下面我们来了解几种类的装饰器。

3、几种类的装饰器

(1)执行顺序

// 第一个装饰器
function testDecorator(constructor: any) {
   
  console.log('decorator');
}

// 第二个装饰器
function testDecorator1(constructor: any) {
   
  console.log('decorator1');
}

// 装饰器执行的时候,是从下到上,从右到左的顺序
@testDecorator
@testDecorator1
class Test {
   }

const test = new Test(); // decorator1 decorator

装饰器执行的时候,是从下到上,从右到左的顺序。

(2)参数判断

我们如何让类装饰器接收一个参数呢?来看一段代码:

// 外面再包一层函数
function testDecorator(flag: boolean) {
   
  // 工厂模式
  if (flag) {
   
    return function (constructor: any) {
   
      constructor.prototype.getName = () => {
   
        console.log('Monday');
      };
    };
  } else {
   
    return function (constructor: any) {
   };
  }
}

@testDecorator(true)
class Test {
   }

const test = new Test();
(test as any).getName(); // Monday

通过上面这段代码我们可以了解到,我们通过对类装饰器的外部再包上一层函数,这其实有点像柯里化的形式,之后通过外部的这个函数进行传参,也就是上面代码中的 flag 。最终类装饰器返回一个函数作为结果,顺利地进行传参。

(3)装饰器标准写法

上面的两个装饰器属于两个比较简单和不太规范的装饰器。下面我们来展现一种比较标准的写法:

function testDecorator() {
   
  return function <T extends new (...args: any[]) => any>(constructor: T) {
   
    return class extends constructor {
   
      name = 'Tuesday';
      getName() {
   
        return this.name;
      }
    };
  };
}

const Test = testDecorator()(
  class {
   
    name: string;
    constructor(name: string) {
   
      this.name = name;
    }
  }
);

const test = new Test('Monday');
console.log(test.getName()); // Tuesday

在上面的代码中, (...args: any[]) => any 是一个函数,返回值是一个对象的类型。这个函数会接收很多参数,函数把这些参数合并到一起,变成一个数组,也就是 ...args 。那 <T extends new (...args: any[]) => any> 是什么意思呢?意思是, T 可以通过 new (...args: any[]) => any 这种类型的构造函数,给实例化出来。所以 T 现在可以理解为是一个类或者是 constructor 这样的一个构造函数。

最终,我们通过 testDecorator()() 这样的方式,让 test 实例可以访问到 getName() 方法,并打印出 Tuesday

🤐二、类的其他装饰器

1、方法装饰器

这里我想要强调的一个问题是,大家觉得,类装饰器的执行时刻是什么样的?类装饰器在类定义完成之后就可以立即对进行一个装饰。

那方法装饰器,是什么样的呢?

方法装饰器,跟类装饰器也是一样的。它会等类创建好了之后,立即地把方法去做一个修改。

很多小伙伴可能会误认为,我是不是得实例化的时候,才会对方法去做一个装饰呢?其实不是这样的,只要在定义完以后,就会帮助我们对类的方法去做一个装饰。先来看一段代码:

// 普通方法, target 对应的是类的 prototype
// 静态方法, target 对应的是类的 构造函数
function getNameDecorator(
  target: any,
  key: string,
  descriptor: PropertyDescriptor
) {
   
  // console.log(target);
  // descriptor的作用:对方法中的属性做一些编辑
  descriptor.writable = true;
  // 通过调用 .value 的方式,可以对原来的方法做一些变更
  descriptor.value = function () {
   
    return 'decorator';
  };
}

class Test {
   
  name: string;
  constructor(name: string) {
   
    this.name = name;
  }
  @getNameDecorator
  getName() {
   
    return this.name;
  }
}

const test = new Test('Monday');
test.getName = () => {
   
  return '123';
};
console.log(test.getName()); // decorator

大家先看上面这段代码,可能有的小伙伴会觉得,最终打印的是 123 。但其实,因为我们对方法进行了装饰,所以最终打印的结果是 decorator

因此,一个装饰器对一个方法做完装饰之后,就可以多做很多事情了。包括原型target,key值 和 descriptor ,都可以对方法做很多修改。

2、访问器的装饰器

现在,我们来学习类里面中,访问器的装饰器。我们先来看一段代码:

function visitDecorator(
  target: any,
  key: string,
  descriptor: PropertyDescriptor
) {
   
  // console.log(123);
}

class Test {
   
  private _name: string;
  constructor(name: string) {
   
    this._name = name;
  }
  // 这里不能写@visitDecorator,同时写两个会引发报错
  get name() {
   
    return this._name;
  }
  @visitDecorator
  set name(name: string) {
   
    this._name = name;
  }
}

const test = new Test('Monday');
test.name = 'Tuesday';
console.log(test.name); // Tuesday

其中, @visitDecorator 是一个访问器装饰器。我们现在来解释下上面代码中的运行路径。

第一部分, test.name = 'Tuesday' 走的是 set 方法,把 Tuesday 这个值赋值给 name 。之后,等到我们运行 console.log 的时候,就是去调用 get 方法,所以最终打印出来的也就是 Tuesday 而不是 Monday

3、属性的装饰器

我们先来看第一种属性的装饰器。具体代码如下:

function nameDecorator(target: any, key: string): any {
   
  const descriptor: PropertyDescriptor = {
   
    writable: true,
  };
  return descriptor;
}

class Test {
   
  @nameDecorator
  name = 'Monday';
}

const test = new Test();
test.name = 'Tuesday';
console.log(test.name); // Tuesday

属性装饰器的写法,也是一个 decorator 的形式,即上述代码中的 @nameDecorator 。这个装饰器接收两个参数,分别是 原型target属性的名字key 。在这里我们可以返回一个 descriptor 来替换掉属性原始的 descriptor 。替换完成之后,最终打印 Tuesday


继续,我们来看第二种装饰器。具体代码如下:

// 该装饰器无法直接修改实例上的属性值(name),而只能修改原型上的属性值(name)
function nameDecorator(target: any, key: string): any {
   
  target[key] = 'Tuesday';
}

// name存储在类的实例上
class Test {
   
  @nameDecorator
  name = 'Monday';
}

const test = new Test();
test.name = 'Hello~';
console.log((test as any).name); // Hello~
console.log((test as any).__proto__.name); // Tuesday

这种类型的装饰器中,值得注意的点是, nameDecorator 只能用来修改原型上的属性值,而无法直接修改实例上的属性值。

4、参数装饰器

上面我们讲到了对类里面的方法、访问器和属性做修饰,现在,我们再来了解一种新的装饰器:对类里面的方法中的参数做修饰。先来看一段代码:

// 原型,方法名,参数所在的位置
function paramDecorator(target: any, key: string, paramIndex: number): any {
   
  console.log(target, key, paramIndex); // Test { getInfo: [Function] } , 'getInfo' , 1(参数所在位置是第2个位置)
}

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

const test = new Test();
test.getInfo('Monday', 18); // Monday 18

大家可以看到,通过对方法中的参数进行装饰,我们可以获取到装饰器的原型方法名参数所在的位置,这个就是参数装饰器。

😐三、装饰器实际使用的小例子

上面我们讲到了很多种装饰器相关的原理知识,现在我们用一个实际使用的例子来带大家更好的使用装饰器。先看一段代码:

const userInfo: any = undefined;

function catchError(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 {
   
  @catchError('userInfo.name 不存在')
  getName() {
   
    return userInfo.name;
  }
  @catchError('userInfo.age 不存在')
  getAge() {
   
    return userInfo.age;
  }
  @catchError('userInfo.gender 不存在')
  getGender() {
   
    return userInfo.gender;
  }
}

const test = new Test();
test.getName(); // userInfo.name 不存在
test.getAge(); // userInfo.age 不存在
test.getGender(); // userInfo.gender 不存在

在上面的代码中,我们做的是捕获异常的一个功能。通过封装 @catchError 装饰器,来对我们最终使用的三个方法,getNamegetAgegetGender ,对这三个方法进行异常捕获

以上算是对装饰器的一次小小的实践,后续深入学习可以再参考一些书籍去多练习。

😏四、结束语

在上面的文章中,我们讲解了装饰器中最基础的类装饰器,以及类装饰器中的4中其他类型的装饰器。最后,我们还用了一个小例子去简单地了解了,装饰器在实际应用中的一些操作。

到这里,关于装饰器的学习就接近尾声啦!不知道小伙伴们对装饰器又有了一些新的了解呢?

如果您觉得这篇文章有帮助到您的的话不妨点赞支持一下哟~~😉

我们下期再见!👋👋👋

🥳 往期推荐

🛵TypeScript,从0到入门带你进入类型的世界

相关文章
|
11天前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
8天前
|
机器学习/深度学习 算法 大数据
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
2024“华为杯”数学建模竞赛,对ABCDEF每个题进行详细的分析,涵盖风电场功率优化、WLAN网络吞吐量、磁性元件损耗建模、地理环境问题、高速公路应急车道启用和X射线脉冲星建模等多领域问题,解析了问题类型、专业和技能的需要。
2520 17
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
|
7天前
|
机器学习/深度学习 算法 数据可视化
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
2024年中国研究生数学建模竞赛C题聚焦磁性元件磁芯损耗建模。题目背景介绍了电能变换技术的发展与应用,强调磁性元件在功率变换器中的重要性。磁芯损耗受多种因素影响,现有模型难以精确预测。题目要求通过数据分析建立高精度磁芯损耗模型。具体任务包括励磁波形分类、修正斯坦麦茨方程、分析影响因素、构建预测模型及优化设计条件。涉及数据预处理、特征提取、机器学习及优化算法等技术。适合电气、材料、计算机等多个专业学生参与。
1522 15
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
|
3天前
|
存储 关系型数据库 分布式数据库
GraphRAG:基于PolarDB+通义千问+LangChain的知识图谱+大模型最佳实践
本文介绍了如何使用PolarDB、通义千问和LangChain搭建GraphRAG系统,结合知识图谱和向量检索提升问答质量。通过实例展示了单独使用向量检索和图检索的局限性,并通过图+向量联合搜索增强了问答准确性。PolarDB支持AGE图引擎和pgvector插件,实现图数据和向量数据的统一存储与检索,提升了RAG系统的性能和效果。
|
9天前
|
编解码 JSON 自然语言处理
通义千问重磅开源Qwen2.5,性能超越Llama
击败Meta,阿里Qwen2.5再登全球开源大模型王座
578 14
|
1月前
|
运维 Cloud Native Devops
一线实战:运维人少,我们从 0 到 1 实践 DevOps 和云原生
上海经证科技有限公司为有效推进软件项目管理和开发工作,选择了阿里云云效作为 DevOps 解决方案。通过云效,实现了从 0 开始,到现在近百个微服务、数百条流水线与应用交付的全面覆盖,有效支撑了敏捷开发流程。
19282 30
|
10天前
|
人工智能 自动驾驶 机器人
吴泳铭:AI最大的想象力不在手机屏幕,而是改变物理世界
过去22个月,AI发展速度超过任何历史时期,但我们依然还处于AGI变革的早期。生成式AI最大的想象力,绝不是在手机屏幕上做一两个新的超级app,而是接管数字世界,改变物理世界。
483 49
吴泳铭:AI最大的想象力不在手机屏幕,而是改变物理世界
|
1月前
|
人工智能 自然语言处理 搜索推荐
阿里云Elasticsearch AI搜索实践
本文介绍了阿里云 Elasticsearch 在AI 搜索方面的技术实践与探索。
18839 20
|
1月前
|
Rust Apache 对象存储
Apache Paimon V0.9最新进展
Apache Paimon V0.9 版本即将发布,此版本带来了多项新特性并解决了关键挑战。Paimon自2022年从Flink社区诞生以来迅速成长,已成为Apache顶级项目,并广泛应用于阿里集团内外的多家企业。
17529 13
Apache Paimon V0.9最新进展
|
2天前
|
云安全 存储 运维
叮咚!您有一份六大必做安全操作清单,请查收
云安全态势管理(CSPM)开启免费试用
365 4
叮咚!您有一份六大必做安全操作清单,请查收