【TypeScript】扩展:装饰器

文章目录

    • 装饰器
      • 一、类装饰器
          • 1. 基本用法
          • 2. 装饰器返回值
          • 3. 构造类型
          • 4. 替换被装饰的类
      • 二、装饰器工厂
      • 三、装饰器组合
      • 四、属性装饰器
      • 五、方法装饰器
      • 六、访问器装饰器
      • 七、参数装饰器

装饰器

装饰器本质是一种特殊函数,可以对类、属性、方法、参数进行扩展,使代码更简洁。

一、类装饰器

1. 基本用法

Demo函数会在Person类定义时即刻执行;target参数是被装饰的类,即Person

function Demo(target:Function){console.log(target)
}
@Demo //这句话相等于 Demo(Person)
class Person {constructor(public name:string,public age:number){ }
}
  • 应用举例(追加方法)

    function CustomString(target:Function){target.prototype.toString = function(){ //在原型中添加方法return JSON.stringify(this) //作用对象是当前实例,返回JSON格式的数据}
    }
    @CustomString
    class Person{constructor(public name:string,public age:number){ }speak(){console.log(`我叫${this.name}`)}
    }
    const p1 = new Person("张三",18)
    //在 JavaScript 中,几乎所有的对象都有一个 toString() 方法。它通常用于返回对象的字符串表示。
    console.log(p1.toString()) 
    //没有装饰器时输出:[object Object]
    //添加装饰器后输出:{"name":"张三","age":18}
    
  • 封闭原型对象,禁止随意操作原型对象:Object.seal(target.prototype)

2. 装饰器返回值
  • 若有返回值:返回一个新的类,这个新类将会替换掉被装饰的类
  • 若无返回值:undefined,不会被替换
3. 构造类型

构造类型用于描述构造函数的类型,它定义了如何通过 new 关键字创建类的实例。
通俗一点:可以用new关键字调用的类

/*   new :可以被new...arg 可以接受任意数量的参数any 可以接受任意类型的参数=> { }:返回类型时空对象(非null,undefined) */
type Constructor = new (...args:any[]) =>{ }
//现在的需求是,传入的fn得是一个类
function test( fn:Constructor){ }
class p1{ }
const p2 = () => { }
test(p1)
test(p2) //报错
4. 替换被装饰的类

对于高级的装饰器,不仅是覆盖一个原型上的方法,还要用更多功能。

  • 现有需求:设计一个setime装饰器,可以给实例添加一个属性,用于记录实例对象创建的时间,再添加方法读取时间。

    type Constructor = new (...args: any[]) => {} //构造类型
    //接口(自动合并的特性)
    interface Person {printDate():void //让Person知道自己多了一个函数
    }
    function SetTime<T extends Constructor>(target: T) { //装饰的必须是可以new的类return class extends target{ //继承原类,名字不变createTime: Dateconstructor(...args: any[]) { //目的是提高兼容性super(...args)this.createTime = new Date()}printDate(){console.log(`该对象的创建时间是:${this.createTime}`)}}
    }
    @SetTime
    class Person {constructor(public name: string, public age: number) { }
    }
    const p1 = new Person("李华",18)
    p1.printDate() //输出:该对象的创建时间是:${this.createTime}
    

二、装饰器工厂

  • 装饰器工厂不是装饰器,装饰器工厂的返回值是装饰器,装饰器工厂的作用主要是它接收的参数可以供给装饰器使用

  • 现有需求,在原有类上添加一个方法,这个方法能打印name指定次数

    interface User{introduce():void
    }
    function LogName(n:number){return function(target:Function) {target.prototype.introduce = function(){ //在原型上添加方法for(let i = 0 ;i<n;i++){console.log(`我叫${this.name}`) //打印实例姓名}}}
    }
    @LogName(3) //相当于LogName(3)(User) 因为LogName返回值是一个函数(装饰器),所以还可以接收参数
    class User{constructor(public name:string){ }
    }
    const p1 = new User("李华")
    p1.introduce()
    

三、装饰器组合

  • 装饰器组合使用的顺序:

    先【从上到下】执行所有的装饰器工厂,再【从下到上】执行所有装饰器(包括装饰器工厂里的装饰器)

四、属性装饰器

  • 属性装饰器:对属性的行为进行装饰

  • 接收的参数有两个:

    1. 第一个:如果装饰的属性是静态属性则参数为类,否则为类的原型对象
    2. 第二个:修饰的属性名

    补充:什么是static属性?
    答:静态属性是类本身的属性,而不是类的实例的属性。通过类名可以访问静态属性,而无法通过实例来访问

  • 现在有需求,需要在类属性的值修改之际,就打印出修改的信息。这时候就需要用到属性装饰器

    function State(target: object, propertykey: string) {//私有属性,通过p1.age访问的值实际是p1.__age(这是个私有属性,只允许当前用户修改)let key = `__${propertykey}`Object.defineProperty(target, propertykey, { //添加原型属性get() {return this[key]},set(newValue) {console.log(`${propertykey}的最新值是${newValue}`)this[key] = newValue}enumerable:true, //可枚举性configurable:true //可修改性})
    }
    class Person {@State age: numberconstructor(name: number) {this.age = name //当访问this.age时,先在原型链上找是否有这个属性,再做修改}
    }
    
  • 测试用例

    const p1 = new Person(18)
    const p2 = new Person(20)
    p1.age = 20 //输出:age的最新值是20
    p2.age = 30 //输出:age的最新值是30
    console.log(p1.age) //输出:20
    console.log(p2.age) //输出:30
    

五、方法装饰器

  • 接收的参数有三个:

    1. 第一个:如果装饰的方法是静态方法则参数为类,否则为类的原型对象
    2. 第二个:装饰的方法名
    3. 第三个:方法的描述对象,其中的value值是被装饰的方法
  • 应用举例:在函数执行时,打印函数执行日志

    function Logger(target: object, propertykey: string, descriptor: PropertyDescriptor) {//存储原始方法const origin = descriptor.value//替换原始方法descriptor.value = function (...args: any[]) {console.log("函数开始执行...")const result = origin.call(this, ...args) //绑定this指向实例对象,预防this可能发生上下文丢失console.log("函数执行结束...")return result //以免调用的参数是有返回值得,增加兼容性}
    }class info {constructor(public name: string) { }@Logger speak() { console.log(`我是${this.name}函数`) }
    }
    
  • 测试用例

    class info {constructor(public name: string) { }@Logger speak() { console.log(`我是${this.name}函数`) }
    }
    const info1 = new info("模幂运算")
    info1.speak()
    /*输出:函数开始执行...我是模幂运算函数函数执行结束...
    */
    
  • 注意点:.call().apply()方法的区别

    • 共同点
      1. 都允许改变函数执行时的 this,即控制上下文。
      2. 都能用来调用其他对象的方法,并将该方法的 this 设置为指定的对象。(实际和第一条差不多)
    • 不同点
      1. call 需要逐个列出参数。
      2. apply 需要把参数作为数组传递。

六、访问器装饰器

访问器装饰器的工作机制与普通方法装饰器相似,但它们专门作用于 getter 和 setter。

  • 应用案例:定义一个简单的装饰器,在访问某个属性时打印日志

    function log(target: any, key: string, descriptor: PropertyDescriptor): PropertyDescriptor {const originalGet = descriptor.get;const originalSet = descriptor.set;// 修改 getterdescriptor.get = function() {console.log(`Getting value of ${String(key)}`);return originalGet?.call(this);};// 修改 setterdescriptor.set = function(value: any) {console.log(`Setting value of ${String(key)} to ${value}`);if (originalSet) {originalSet.call(this, value);}};return descriptor;
    }
    class Person {private _name: string = '';@logget name() {return this._name;}@logset name(value: string) {this._name = value;}
    }
    const p = new Person();
    p.name = 'Alice';  // 输出: Setting value of name to Alice
    console.log(p.name);  // 输出: Getting value of name, 然后输出: Alice

七、参数装饰器

参数装饰器(Parameter Decorators)是用来装饰类方法中的参数的装饰器。参数装饰器允许我们为方法参数添加元数据,或者在参数被传递给方法之前执行一些额外的操作。

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

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

相关文章

WSL2中安装的ubuntu搭建tftp服务器uboot通过tftp下载

Windows中安装wsl2&#xff0c;wsl2里安装ubuntu。 1. Wsl启动后 1&#xff09;Windows下ip ipconfig 以太网适配器 vEthernet (WSL (Hyper-V firewall)): 连接特定的 DNS 后缀 . . . . . . . : IPv4 地址 . . . . . . . . . . . . : 172.19.32.1 子网掩码 . . . . . . . .…

一、TensorFlow的建模流程

1. 数据准备与预处理&#xff1a; 加载数据&#xff1a;使用内置数据集或自定义数据。 预处理&#xff1a;归一化、调整维度、数据增强。 划分数据集&#xff1a;训练集、验证集、测试集。 转换为Dataset对象&#xff1a;利用tf.data优化数据流水线。 import tensorflow a…

软件工程概论试题五

一、多选 1.好的软件的基本属性包括()。 A. 效率 B. 可依赖性和信息安全性 C. 可维护性 D.可接受性 正答&#xff1a;ABCD 2.软件工程的三要素是什么()? A. 结构化 B. 工具 C.面向对象 D.数据流! E.方法 F.过程 正答&#xff1a;BEF 3.下面中英文术语对照哪些是正确的、且是属…

问题的价值 ( Value of Question ) 公式

一、什么是问题的价值 我们的人生、工作的期间、瞬息万变的商业环境中&#xff0c;我们必然会面对很多问题&#xff0c;也会提出很多问题。 但这些问题是否具有回答的 价值&#xff0c;应该如何 衡量 呢&#xff1f; 简单如&#xff0c;女朋友问今晚应该吃什么、世界如何才能…

一文了解阿里的 Qwen2.5 模型

最近被DeepSeek刷屏了&#xff0c;但是在之外阿里在2025年1月28日推出了Qwen 2.5 Max模型。 Qwen2.5-Max 的特点&#xff1a;大规模的 MoE 模型&#xff0c;预训练于超 20 万亿 tokens&#xff0c;并经过 SFT 和 RLHF 后训练。 性能表现&#xff1a;在多个基准测试中与领先模型…

基于Django的Boss直聘IT岗位可视化分析系统的设计与实现

【Django】基于Django的Boss直聘IT岗位可视化分析系统的设计与实现&#xff08;完整系统源码开发笔记详细部署教程&#xff09;✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 该系统采用Python作为主要开发语言&#xff0c;利用Django这一高效、安全的W…

Hive:复杂数据类型之Map函数

Map函数 是Hive里面的一种复杂数据类型, 用于存储键值对集合。Map中的键和值可以是基础类型或复合类型&#xff0c;这使得Map在处理需要关联存储信息的数据时非常有用。 定义map时,需声明2个属性: key 和 value , map中是 key value 组成一个元素 key-value, key必须为原始类…

计算机毕业设计Python动漫推荐系统 漫画推荐系统 动漫视频推荐系统 机器学习 bilibili动漫爬虫 数据可视化 数据分析 大数据毕业设计

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

2025年02月02日Github流行趋势

项目名称&#xff1a;oumi 项目地址url&#xff1a;https://github.com/oumi-ai/oumi 项目语言&#xff1a;Python 历史star数&#xff1a;1416 今日star数&#xff1a;205 项目维护者&#xff1a;xrdaukar, oelachqar, taenin, wizeng23, kaisopos 项目简介&#xff1a;构建最…

谭浩强C语言程序设计(3) 7章

1、递归实现N的阶乘 c复制 #include <cstdio> // 包含标准输入输出库// 计算n的阶乘 int total 0; // 定义全局变量total用于存储阶乘结果// 递归函数计算阶乘 int fac(int a){// 如果输入的数小于0&#xff0c;输出错误信息if (a < 0){printf("%d < 0,err…

python算法和数据结构刷题[2]:链表、队列、栈

链表 链表的节点定义&#xff1a; class Node():def __init__(self,item,nextNone):self.itemitemself.nextNone 删除节点&#xff1a; 删除节点前的节点的next指针指向删除节点的后一个节点 添加节点&#xff1a; 单链表 class Node():"""单链表的结点&quo…

网络工程师 (13)时间管理

一、定义与重要性 项目时间管理是指为确保项目按时完成而采取的一系列规划、安排和控制活动。它始于项目启动阶段&#xff0c;贯穿整个项目生命周期&#xff0c;直至项目结束。时间管理对于项目的成功至关重要&#xff0c;它有助于项目团队明确工作目标和时间节点&#xff0c;增…

2025.2.1——四、php_rce RCE漏洞|PHP框架

题目来源&#xff1a;攻防世界 php_rce 目录 一、打开靶机&#xff0c;整理信息 二、解题思路 step 1&#xff1a;PHP框架漏洞以及RCE漏洞信息 1.PHP常用框架 2.RCE远程命令执行 step 2&#xff1a;根据靶机提示&#xff0c;寻找版本漏洞 step 3&#xff1a;进行攻击…

记录 | 基于MaxKB的文字生成视频

目录 前言一、安装SDK二、创建视频函数库三、调试更新时间 前言 参考文章&#xff1a;如何利用智谱全模态免费模型&#xff0c;生成大家都喜欢的图、文、视并茂的文章&#xff01; 自己的感想 本文记录了创建文字生成视频的函数库的过程。如果想复现本文&#xff0c;需要你逐一…

Error: Expected a mutable image

你的函数用了不支持的图片格式比如我的人脸检测&#xff0c;本来要RGB565我却用JPEG所以报错

深度学习查漏补缺:1.梯度消失、梯度爆炸和残差块

一、梯度消失 梯度消失的根本原因在于 激活函数的性质和链式法则的计算&#xff1a; 激活函数的导数很小&#xff1a; 常见的激活函数&#xff08;例如 Sigmoid 和 Tanh&#xff09;在输入较大或较小时&#xff0c;输出趋于饱和&#xff08;Sigmoid 的输出趋于 0 或 1&#xf…

c++可变参数详解

目录 引言 库的基本功能 va_start 宏: va_arg 宏 va_end 宏 va_copy 宏 使用 处理可变参数代码 C11可变参数模板 基本概念 sizeof... 运算符 包扩展 引言 在C编程中&#xff0c;处理不确定数量的参数是一个常见的需求。为了支持这种需求&#xff0c;C标准库提供了 &…

【自学嵌入式(8)天气时钟:天气模块开发、主函数编写】

天气时钟&#xff1a;天气模块开发、主函数编写 I2C协议和SPI协议I2C&#xff08;Inter-Integrated Circuit&#xff09;SPI&#xff08;Serial Peripheral Interface&#xff09; 天气模块心知天气预报使用HTTPClient类介绍主要功能常用函数注意事项 JSON介绍deserializeJson函…

SpringBoot的配置(配置文件、加载顺序、配置原理)

文章目录 SpringBoot的配置(配置文件、加载顺序、配置原理)一、引言二、配置文件1、配置文件的类型1.1、配置文件的使用 2、多环境配置 三、加载顺序四、配置原理五、使用示例1、配置文件2、配置类3、控制器 六、总结 SpringBoot的配置(配置文件、加载顺序、配置原理) 一、引言…

政务行业审计文件大数据高速报送解决方案

随着信息技术的快速发展&#xff0c;电子政务迎来了新的升级浪潮。国家相继出台了一系列信息化发展战略规划&#xff0c;如《国家信息化发展战略纲要》、《“十三五”国家信息化规划》等&#xff0c;这些政策为政务信息化工作指明了方向。 然而&#xff0c;在实际操作中&#x…