面试题-TS(八):什么是装饰器(decorators)?如何在 TypeScript 中使用它们?

面试题-TS(八):什么是装饰器(decorators)?如何在 TypeScript 中使用它们?

在TypeScript中,装饰器(Decorators)是一种用于增强代码功能的特殊类型声明。装饰器提供了一种在类、方法、属性等代码元素上注释或修改的方式,使得我们可以通过装饰器来扩展、修改或监视代码的行为。通过使用装饰器,我们可以在不修改原始代码的情况下,给代码添加新的功能,提高代码的可维护性和灵活性。

一、什么是装饰器?

装饰器是一种特殊类型的声明,它以@符号为前缀,后跟一个表达式,通常是一个函数。装饰器可以附着在类、方法、属性等代码元素上,并在运行时对这些元素进行注释或修改。

在TypeScript中,装饰器的使用主要通过以下两种方式:

  1. 类装饰器:装饰类的声明。
  2. 方法装饰器、属性装饰器、参数装饰器:装饰类中的方法、属性和参数。

二、如何定义装饰器?

装饰器本质上是一个函数,它接收三个参数:

  1. 对于类装饰器,它的参数是类的构造函数。
  2. 对于方法装饰器,它的参数是类的原型对象、方法名和方法的属性描述符。
  3. 对于属性装饰器,它的参数是类的原型对象和属性名。
  4. 对于参数装饰器,它的参数是类的原型对象、方法名和参数的索引。

装饰器函数可以根据这些参数来获取或修改类、方法、属性或参数的信息,并实现相应的功能。

以下是一个简单的装饰器示例,用于在类上添加一个日志:

function logClass(target: Function) {console.log("Class logged: ", target);
}@logClass
class MyClass {// ...
}

在上面的示例中,我们定义了一个装饰器函数logClass,它接收类的构造函数target作为参数,并在控制台输出类的构造函数。然后我们使用装饰器@logClass来装饰MyClass类。

三、如何在 TypeScript 中使用装饰器?

要在TypeScript中使用装饰器,首先需要在tsconfig.json中启用experimentalDecorators选项:

{"compilerOptions": {"experimentalDecorators": true}
}

然后,我们就可以在类、方法、属性和参数上使用装饰器了。

类装饰器

类装饰器是应用于类声明的装饰器。它会在类声明时调用,并接收类的构造函数作为参数。类装饰器通常用于修改或扩展类的行为。

以下是一个简单的类装饰器示例,用于添加一个静态属性:

function addStaticProperty(target: Function) {target.staticProperty = "This is a static property.";
}@addStaticProperty
class MyClass {// ...
}console.log(MyClass.staticProperty); // 输出:This is a static property.

在上面的示例中,我们定义了一个类装饰器addStaticProperty,它在类声明时添加了一个静态属性staticProperty。然后我们使用装饰器@addStaticProperty来装饰MyClass类,并在控制台输出静态属性的值。

方法装饰器

方法装饰器是应用于类方法的装饰器。它会在方法声明时调用,并接收类的原型对象、方法名和方法的属性描述符作为参数。方法装饰器通常用于修改或监视方法的行为。

以下是一个简单的方法装饰器示例,用于添加一个日志:

function logMethod(target: any, methodName: string, descriptor: PropertyDescriptor) {const originalMethod = descriptor.value;descriptor.value = function (...args: any[]) {console.log("Method logged: ", methodName);return originalMethod.apply(this, args);};
}class MyClass {@logMethodgreet(name: string) {return `Hello, ${name}!`;}
}const myClass = new MyClass();
myClass.greet("John"); // 输出:Method logged: greet// 输出:Hello, John!

在上面的示例中,我们定义了一个方法装饰器logMethod,它在方法声明时添加了一个日志功能。然后我们使用装饰器@logMethod来装饰MyClass类的greet方法。

属性装饰器

属性装饰器是应用于类属性的装饰器。它会在属性声明时调用,并接收类的原型对象和属性名作为参数。属性装饰器通常用于修改或监视属性的行为。

以下是一个简单的属性装饰器示例,用于添加一个日志:

function logProperty(target: any, propertyName: string) {let value = target[propertyName];const getter = function () {console.log("Property logged: ", propertyName);return value;};const setter = function (newVal: any) {console.log("Property set: ", propertyName);value = newVal;};Object.defineProperty(target, propertyName, {get: getter,set: setter,});
}class MyClass {@logPropertymessage: string = "Hello";
}const myClass = new MyClass();
console.log(myClass.message); // 输出:Property logged: message// 输出:HellomyClass.message = "World";     // 输出:Property set: message
console.log(myClass.message); // 输出:Property logged: message// 输出:World

在上面的示例中,我们定义了一个属性装饰器logProperty,它在属性声明时添加了一个日志功能。然后我们使用装饰器@logProperty来装饰MyClass类的message属性。

参数装饰器

参数装饰器是应用于类方法参数的装饰器。它会在方法参数声明时调用,并接收类的原型对象、方法名和参数的索引作为参数。参数装饰器通常用于修改或监视方法参数的行为。

以下是一个简单的参数装饰器示例,用于添加一个日志:

function logParameter(target: any, methodName: string, parameterIndex: number) {console.log("Parameter logged: ", methodName, parameterIndex);
}class MyClass {greet(@logParameter name: string) {return `Hello, ${name}!`;}
}const myClass = new MyClass();
myClass.greet("John"); // 输出:Parameter logged: greet 0// 输出:Hello, John!

在上面的示例中,我们定义了一个参数装饰器logParameter,它在方法参数声明时添加了一个日志功能。然后我们使用装饰器@logParameter来装饰MyClass类的greet方法的name参数。

四、装饰器组合

在TypeScript中,我们可以将多个装饰器组合在一起使用。装饰器组合的顺序是从上到下执行的,即从外到内。

以下是一个装饰器组合的示例:

function logClass(target: Function) {console.log("Class logged: ", target);
}function addStaticProperty(target: Function) {target.staticProperty = "This is a static property.";
}@logClass
@addStaticProperty
class MyClass {// ...
}console.log(MyClass.staticProperty); // 输出:This is a static property.

在上面的示例中,我们先使用装饰器@addStaticProperty来装饰MyClass类,然后再使用装饰器@logClass来装饰它。由于装饰器组合的顺序是从外到内执行的,所以先执行的是@addStaticProperty装饰器,再执行的是@logClass装饰器。

五、自定义装饰器

除了使用已有的装饰器,我们还可以自定义装饰器来实现特定的功能。

以下是一个简单的自定义装饰器示例,用于计算方法执行的时间:

function logExecutionTime(target: any, methodName: string, descriptor: PropertyDescriptor) {const originalMethod = descriptor.value;descriptor.value = function (...args: any[]) {console.time(methodName);const result = originalMethod.apply(this, args);console.timeEnd(methodName);return result;};
}class MyClass {@logExecutionTimelongRunningTask() {// 模拟耗时任务for (let i = 0; i < 1000000000; i++) {}}
}const myClass = new MyClass();
myClass.longRunningTask(); // 输出:longRunningTask: 2804.869ms

在上面的示例中,我们定义了一个自定义装饰器logExecutionTime,它在方法执行前后添加了计时功能。然后我们使用装饰器@logExecutionTime来装饰MyClass类的longRunningTask方法。

六、装饰器工厂

装饰器工厂是一个返回装饰器的函数。它可以接收参数,并返回一个装饰器函数。

以下是一个简单的装饰器工厂示例,用于添加一个指定的前缀:

function addPrefix(prefix: string) {return function (target: any, propertyName: string) {const value = target[propertyName];Object.defineProperty(target, propertyName, {get: function () {return prefix + value;},set: function (newVal: any) {value = newVal;},});};
}class MyClass {@addPrefix("Hello, ")message: string = "World";
}const myClass = new MyClass();
console.log(myClass.message); // 输出:Hello, World

在上面的示例中,我们定义了一个装饰器工厂addPrefix,它返回一个装饰器函数,用于在属性的值前面添加指定的前缀。然后我们使用装饰器@addPrefix("Hello, ")来装饰MyClass类的message属性。

七、装饰器的应用场景

装饰器在代码中有许多应用场景。以下是一些常见的用例:

  1. 日志记录:在方法或类上添加日志功能,用于记录方法的执行过程和结果。

  2. 性能监控:在方法或类上添加性能监控功能,用于计算方法的执行时间。

  3. 权限验证:在方法或类上添加权限验证功能,用于检查用户是否有权执行某个操作。

  4. 数据验证:在方法或类上添加数据验证功能,用于检查输入数据是否合法。

  5. 缓存处理:在方法或类上添加缓存处理功能,用于缓存方法的结果。

总结

在TypeScript中,装饰器是一种用于增强代码功能的特殊类型声明。装饰器提供了一种在类、方法、属性等代码元素上注释或修改的方式,使得我们可以通过装饰器来扩展、修改或监视代码的行为。通过使用装饰器,我们可以在不修改原始代码的情况下,给代码添加新的功能,提高代码的可维护性和灵活性。装饰器有类装饰器、方法装饰器、属性装饰器和参数装饰器等几种类型,每种类型有不同的使用场景和应用方式。让我们充分利用装饰器的优势,提高我们的代码功能和可读性,构建出更健壮和可维护的应用程序。

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

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

相关文章

每日一道面试题之如何实现数组和 List 之间的转换?

要实现数组和List之间的转换&#xff0c;可以使用Java中的Arrays类和Collections类提供的方法。 数组转换为List&#xff1a; 使用Arrays类的asList()方法可以将数组转换为List。这个方法接受一个数组作为参数&#xff0c;并返回一个包含数组元素固定大小的List。 举例&…

EMP-SSL: TOWARDS SELF-SUPERVISED LEARNING IN ONETRAINING EPOCH

Recently, self-supervised learning (SSL) has achieved tremendous success in learning image representation. Despite the empirical success, most self-supervised learning methods are rather “inefficient” learners, typically taking hundreds of training epoch…

TCP状态转换图

TCP状态转换图 了解TCP状态转换图可以帮助开发人员查找问题. 说明: 上图中粗线表示主动方, 虚线表示被动方, 细线部分表示一些特殊情况, 了解即可, 不必深入研究. 对于建立连接的过程客户端属于主动方, 服务端属于被动接受方(图的上半部分) 而对于关闭(图的下半部分), 服务端…

常用的Docker命令和使用方法

目录 拉取&#xff08;Pull&#xff09;镜像 查看已下载的镜像 创建运行容器 列出正在运行的容器 停止和启动容器 进入容器 查看容器信息 查看容器日志 删除容器和镜像 拉取&#xff08;Pull&#xff09;镜像 当我们在Docker中部署一个应用程序时&#xff0c;需要使用…

政策加持智能家居市场,涂鸦赋能客户打造“以人为本”智能生活新方式

7月18日&#xff0c;商务部等13部门联合发布了《关于促进家居消费若干措施的通知》&#xff08;以下简称《通知》&#xff09;&#xff0c;《通知》指出&#xff0c;创新培育智能消费&#xff0c;支持企业运用物联网、云计算、人工智能等技术&#xff0c;着重加快智能家电、智能…

maven的基本学习

maven https://www.bilibili.com/video/BV14j411S76G?p1&vd_source5c648979fd92a0f7ba8de0cde4f02a6e 1.简介 1.1介绍 Maven翻译为"专家"、“内行”&#xff0c;是Apache下的一个纯Java开发的开源项目。基于项目对象模型(缩写:POM)概念&#xff0c;Maven利用一…

无涯教程-jQuery - jQuery.get( url, data, callback, type )方法函数

jQuery.get(url&#xff0c;[data]&#xff0c;[callback]&#xff0c;[type])方法使用GET HTTP请求从服务器加载数据。 该方法返回XMLHttpRequest对象。 jQuery.get( url, [data], [callback], [type] ) - 语法 $.get( url, [data], [callback], [type] ) 这是此方法使用的…

【数据结构】实验二:顺序表

实验二 顺序表 一、实验目的与要求 1&#xff09;熟悉顺序表的类型定义&#xff1b; 2&#xff09;熟悉顺序表的基本操作&#xff1b; 3&#xff09;灵活应用顺序表解决具体应用问题。 二、实验内容 1&#xff09;在一个整数序列a1,a2,…,an中&#xff0c;若存在一个数&…

【Linux网络】 网络套接字(三)socket编程_TCP网络程序

目录 TCP网络程序服务端创建套接字并绑定服务端监听服务端获取连接服务器处理请求 客户端客户端创建套接字客户端连接服务器客户端发起请求测试 服务器存在的问题多进程版的TCP网络程序多线程版的TCP网络程序线程池版的TCP网络程序 TCP网络程序总结图 TCP网络程序 服务端 创建…

腾讯云内存型CVM服务器MA3、M6、M6ce和M5处理器CPU说明

腾讯云内存型CVM服务器CPU处理器大全&#xff0c;CVM内存型MA3、内存型M6、安全增强内存型M6ce、内存型M6p、内存型M5、MA2、M4、M3、M2、M1处理器主频、CPU性能性能大全说明&#xff0c;腾讯云内存型云服务器具有大内存的特点&#xff0c;适合高性能数据库、分布式内存缓存等需…

Dubbo

Dubbo 简介Dubbo的快速入门Dubbo的基本架构安装DubboAdmin入门案例Dubbo的最佳实践 Dubbo的高级特性启动检查多版本超时与重试负载均衡SpringCloud整合Dubbo案例 简介 Dubbo是阿里巴巴公司开源的一个高性能、轻量级的Java RPC框架。 致力于提高性能和透明化的RPC远程服务调用方…

【云原生】Kubernetes之 Pod hook(钩子)

Pod hook介绍 Pod hook&#xff08;钩子&#xff09;是由 Kubernetes 管理的 kubelet 发起的&#xff0c;当容器中的进程启动前或者容器中的进程终止之前运行&#xff0c;这是包含在容器的生命周期之中。可以同时为 Pod 中的所有容器都配置 hook。 Hook 的类型包括两种&#…

在CSDN学Golang云原生(gitlab)

一&#xff0c;基于Docker安装gitlab runner 在Golang中&#xff0c;基于Docker安装GitLab Runner需要以下步骤&#xff1a; 首先&#xff0c;您需要安装Docker和Docker Compose。这可以通过访问官方网站来完成。接下来&#xff0c;您需要创建一个名为docker-compose.yml的文…

Jenkins+Docker+Docker-Compose自动部署,SpringCloud架构公共包一个任务配置

前言 Jenkins和docker的安装&#xff0c;随便百度吧&#xff0c;实际场景中我们很多微服务的架构&#xff0c;都是有公共包&#xff0c;肯定是希望一个任务能够把公共包的配置加进去&#xff0c;一并构建&#xff0c;ok&#xff0c;直接上干货。 Jenkins 全局环境安装 pwd e…

DSA之图(4):图的应用

文章目录 0 图的应用1 生成树1.1 无向图的生成树1.2 最小生成树1.2.1 构造最小生成树1.2.2 Prim算法构造最小生成树1.2.3 Kruskal算法构造最小生成树1.2.4 两种算法的比较 1.3 最短路径1.3.1 两点间最短路径1.3.2 某源点到其他各点最短路径1.3.3 Dijkstra1.3.4 Floyd 1.4 拓扑排…

机器学习:Bert and its family

Bert 先用无监督的语料去训练通用模型&#xff0c;然后再针对小任务进行专项训练学习。 ELMoBertERNIEGroverBert&PALS Outline Pre-train Model 首先介绍预训练模型&#xff0c;预训练模型的作用是将一些token表示成一个vector 比如&#xff1a; Word2vecGlove 但是对于…

微服务契约测试框架-Pact

契约测试 契约测试的思想就是将原本的 Consumer 与 Provider 间同步的集成测试&#xff0c;通过契约进行解耦&#xff0c;变成 Consumer 与 Provider 端两个各自独立的、异步的单元测试。 契约测试的优点&#xff1a; 契约测试与单元测试以及其它测试之间没有重复&#xff0c…

Google Earth Engine谷歌地球引擎提取多波段长期反射率数据后绘制折线图并导出为Excel

本文介绍在谷歌地球引擎GEE中&#xff0c;提取多年遥感影像多个不同波段的反射率数据&#xff0c;在GEE内绘制各波段的长时间序列走势曲线图&#xff0c;并将各波段的反射率数据与其对应的成像日期一起导出为.csv文件的方法。 本文是谷歌地球引擎&#xff08;Google Earth Engi…

图为科技T501赋能工业机器人 革新传统工业流程

工业机器人已成为一个国家制造技术与科技水平的重要衡量标准&#xff0c;在2019年&#xff0c;中国工业机器人的组装量与产量均位居了全球首位。 当前&#xff0c;工业机器人被广泛用于电子、物流、化工等多个领域之中&#xff0c;是一种通过电子科技和机械关节制作出来的智能机…

浏览器端代理proxy 解决跨域

一.环境:使用expresshttp-proxy-middleware 直接上代码 // include dependencies const express require( express);//node内置的path模块导入 const path require("path")const { createProxyMiddleware } require( http-proxy-middleware); // 需要代理后端服…