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

TypeScript中的装饰器

  • 👏序言
  • 😉一、类的装饰器
    • 1、什么是装饰器
    • 2、装饰器的特点
    • 3、几种类的装饰器
      • (1)执行顺序
      • (2)参数判断
      • (3)装饰器标准写法
  • 🤐二、类的其他装饰器
    • 1、方法装饰器
    • 2、访问器的装饰器
    • 3、属性的装饰器
    • 4、参数装饰器
  • 😐三、装饰器实际使用的小例子
  • 😏四、结束语
  • 🥳 往期推荐

👏序言

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;}@getNameDecoratorgetName() {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;}@visitDecoratorset 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 {@nameDecoratorname = '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 {@nameDecoratorname = '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到入门带你进入类型的世界

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/307788.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode93. 复原 IP 地址

一&#xff1a;每日论语解读 follow me !! 君子坦荡荡 小人常威威 二:题目 三:上码 class Solution { public:/**思路:1.分析题意本题依然是切割字符串(我们需要对要分割的字符串做出处理 需要判断我们截取的字符串是否合法)2。关于‘.’的处理 我们是在原字符串的基础上进…

ASP.NET Core整合Zipkin链路跟踪

前言在日常使用ASP.NET Core的开发或学习中&#xff0c;如果有需要使用链路跟踪系统&#xff0c;大多数情况下会优先选择SkyAPM。我们之前也说过SkyAPM设计确实比较优秀&#xff0c;巧妙的利用DiagnosticSource诊断跟踪日志&#xff0c;可以做到对项目无入侵方式的集成。其实还…

探秘react,一文弄懂react的基本使用和高级特性

一文详解react的基本使用、高级特性和周边插件⏰序言&#x1f4dd;一、React的基本使用1、JSX基本使用&#xff08;1&#xff09;变量、表达式&#xff08;2&#xff09;class和style&#xff08;3&#xff09;子元素和组件&#xff08;4&#xff09;原生 html2、条件判断&…

使用React hooks,些许又多了不少摸鱼时间

一文详解react-hooks&#x1f399;️前言一、&#x1f4fb;概述1、关于React Hooks2、认识React Hooks&#xff08;1&#xff09;回顾React函数式组件&#xff08;2&#xff09;函数组件的特点&#xff08;3&#xff09;class组件的问题&#xff08;4&#xff09;React 组件二、…

手把手教学之如何设计财务对账系统

在设计一个对账系统前&#xff0c;我们先想清楚&#xff0c;对账目的、方式、对账场景、使用对象。其次就是和财务了解一下&#xff0c;他们所期望的效果&#xff0c;毕竟他们是使用方。财务对账是一个琐碎而复杂的工作&#xff0c;极需要工作耐心和细心&#xff0c;还要求对账…

C# 中 Struct 和 Class 的区别总结

翻译自 Manju lata Yadav 2019年6月2日 的博文 《Difference Between Struct And Class In C#》&#xff0c;补充了一些内容和示例。结构体&#xff08;struct&#xff09;是类(class)的轻量级版本。结构体是值类型&#xff0c;可用于创建行为类似于内置类型的对象。比较结构体…

不平凡的2021,末流普本生秋招上岸大厂的历程

&#x1f4fb;叮&#xff01; 2021年接近尾声&#xff0c;周一也开始拾起了年终总结。 回顾2021&#xff0c;有喧嚣&#xff0c;也有欢呼&#xff1b;有奔溃&#xff0c;也有快乐。 但好在✏️ 对待一件又一件的小事上&#xff0c;始终保持着对自己的要求&#x1f4ca; 不…

leetcode491. 递增子序列

一&#xff1a;论语 二:题目 三&#xff1a;上码 class Solution { public:vector<vector<int> >ans;vector<vector<int> >ans1;vector<int> path;void backstacking(vector<int>& nums,int index) {if(path.size() > 2) {ans.pus…

ASP.NET Core 性能优化最佳实践

本文提供了 ASP.NET Core 的性能最佳实践指南。译文原文地址&#xff1a;https://docs.microsoft.com/en-us/aspnet/core/performance/performance-best-practices?viewaspnetcore-3.1积极利用缓存这里有一篇文档在多个部分中讨论了如何积极利用缓存。有关详细信息&#xff0c…

[算法与数据结构] 谈谈线性查找法~

[算法与数据结构] 一文详解线性查找法~&#x1f4c5;前言一、&#x1f4dd;算法基础知识1、什么是算法2、算法的五大特性二、&#x1f4c8;线性查找法1、举例阐述2、实现线性查找法3、使用泛型4、升级改造5、使用自定义类6、循环不变量三、&#x1f4ca;算法复杂度1、简单的复杂…

送福利 | 送书5本《ASP.NET Core项目开发实战入门》带你走进ASP.NET Core开发

《ASP.NET Core项目开发实战入门》从基础到实际项目开发部署带你走进ASP.NET Core开发。ASP.NET Core项目开发实战入门是基于ASP.NET Core 3.1 所写&#xff0c;后续ASP.NET Core 5也会对应更新。简介本书共14章&#xff0c;深入浅出地介绍了ASP.NET Core基础及项目开发方面的知…

海有舟可渡、山有路可循‍‍ —— 大学四年圆满落幕

⛺️前言 仅以此篇文章记录我的大学四年&#xff01; 回忆大学四年&#xff0c;有惊喜、有快乐、有崩溃、有欢呼&#xff1b; 有过无数个为了赶策划、赶ddl的熬夜&#xff0c;更有凌晨2点做志愿活动、凌晨5点赶项目的通宵达旦。 梳理下大学四年的大事件&#xff1a; 大一 …

程序开发天团必备单品,稳定输出还加持千元商务礼限时送!

有这么一个门派他们用强大的思维逻辑频出“大招”用抗造的硬核体能昼夜练习一水儿的格子衫下&#xff0c;个个骨骼精奇深居简出&#xff0c;却默默改变着世界格局他们都使用同一件武器拼的就是个配置高低这与能否称霸武林有着直接关系看看各位高手怎么说“大内”高手 武功唯快不…

了解微前端,深入前端架构的前世今生

前端架构的前世今生&#x1f6f5;前言一、&#x1f6f4;前端架构的前世今生1、架构是如何产生的&#xff1f;2、MVC架构3、前后端分离架构4、Nodejs5、单页面架构&#xff08;1&#xff09;现有单页面架构&#xff08;2&#xff09;单页面架构的优势&#xff08;3&#xff09;单…

leetcode37. 解数独

一:论语 简而言之 就是要一视同仁 对待遇见所有的人要一个态度 二&#xff1a;题目 三:上码 class Solution { public:bool backstacking(vector<vector<char> >& board) {for(int i 0; i < board.size(); i) {for(int j 0; j < board[0].size(); …

跟我一起学.NetCore之.NetCore概述

随着.NetCore版本发布变更&#xff0c;在最近一年左右开始接触到.NetCore&#xff0c;之前只是传闻&#xff0c;并没有动手实操&#xff1b;.NetCore逐渐成熟&#xff0c;大大小小的公司也开始进行使用&#xff0c;感觉再不学习就落后了&#xff0c;于是乎搜索各种资料开始学习…

线程与线程池(一条龙详解)

一:前言 一个问题引出的学习笔记 并发类库提供的线程池实现有哪些? 其实Executors已经为我们封装好了 4 种常见的功能线程池&#xff0c;如下&#xff1a; 定长线程池&#xff08;FixedThreadPool&#xff09;定时线程池&#xff08;ScheduledThreadPool &#xff09;可缓存…

项目升级,无缝对接 .NET 5

开启.NET5时代2020-09-14从NetCore1.1开始学起&#xff0c;然后又从2.0开始讲知识&#xff0c;再到将所有的在线项目升级并长期维护到3.1&#xff0c;转眼已经三年了&#xff0c;一直紧跟着微软的节奏有条不紊的往前走&#xff0c;我相信&#xff0c;只要是从18年末或者19年初跟…

小团队前端部署演化之路

前言 前端部署相对来说其实是一件非常容易的事情&#xff0c;无论是最原始的html页面&#xff0c;还是现在热门的三大框架&#xff0c;最后交付部署的时候&#xff0c;始终会是一些静态文件。虽然简单&#xff0c;但是对于不同的团队来说&#xff0c;都会在不同阶段有最适合他们…

GitHub 全域数字年报:携手推动开源世界的超级协作

2020年1月24日&#xff0c;Wuhan2020开源项目正式发起&#xff0c;在疫情期间累积吸引到了约3000余位技术志愿者以及近1000余位非技术志愿者在线上开展志愿行动与参与。Wuhan2020在成立后的约3个月时间内&#xff0c;通过开源协作的方式在互联网上开展志愿者支持与工作协同&…