当前位置: 首页 > news >正文

阮一峰《TypeScript 教程》学习笔记——装饰器

1. 一段话总结

TypeScript 装饰器是定义时修改类(及成员)行为的语法结构,核心特征为**@前缀+函数表达式**(表达式需最终返回函数),接受value(被装饰对象)和context(上下文,含kind/name等关键属性)两个参数,要么无返回值要么返回新对象替代目标;TS 5.0 同时支持标准语法(直接使用)和传统语法(需开启--experimentalDecorators编译参数);包含类、方法、属性、getter、setter、accessor 六种类型,执行分评估阶段(计算@后表达式得装饰器函数)和应用阶段(按“方法→静态属性→实例属性→类”顺序应用,多装饰器内层先执行);核心用途是简洁扩展类能力(如添加方法、延迟执行、绑定this),但需注意不同装饰器的参数差异(如属性装饰器value恒为undefined)。


2. 思维导图

在这里插入图片描述


3. 详细总结

一、装饰器基础

  1. 定义与核心作用
    装饰器(Decorator)是 TypeScript 中用于在定义阶段修改类及成员行为的语法结构,无需创建子类即可扩展类能力(如添加方法、改写逻辑),比子类继承更简洁。

  2. 语法特征

    特征说明示例
    前缀必须以@开头,后接表达式@log@delay(1000)
    表达式要求最终需返回函数(装饰器函数)@myFuncFactory(123)(工厂函数返回装饰器)
    参数自动接收value(被装饰对象)和context(上下文)function decorator(value: any, context: any) {}
    返回值要么无返回值,要么返回新对象替代被装饰目标方法装饰器返回新函数替代原方法
  3. 版本差异(TS 5.0)
    TS 5.0 支持两种装饰器语法,核心区别如下:

    语法类型启用方式适用场景
    标准语法直接使用,无需编译参数新项目,遵循ECMAScript标准
    传统语法需开启--experimentalDecorators参数(如tsc --target ES5 --experimentalDecorators维护旧TS项目

二、装饰器结构(核心:函数与参数)

装饰器本质是一个函数,类型定义如下,关键在于valuecontext两个参数:

type Decorator = (value: DecoratedValue, // 被装饰的对象(如类、方法)context: { // 上下文对象,描述被装饰对象的元信息kind: string; // 装饰类型(class/method/field等)name: string | symbol; // 被装饰对象的名称addInitializer?(initializer: () => void): void; // 添加初始化逻辑static?: boolean; // 是否为静态成员private?: boolean; // 是否为私有成员access: { get?(): unknown; set?(value: unknown): void }; // 存取器}
) => void | ReplacementValue; // 无返回值或返回新对象替代目标
  • context 核心属性kind(必选,区分装饰类型)和name(必选,对象名称)是所有装饰器共有的,其他属性(如static/private)仅特定装饰器有(如方法装饰器含static)。
  • addInitializer 作用:替代构造函数中的初始化逻辑(如绑定this),在类初始化时执行。

三、六种装饰器类型详解

六种装饰器对应不同的类成员,核心差异体现在value参数、context.kind及作用场景,具体对比如下:

装饰器类型value 参数context.kind核心作用典型场景返回值
类装饰器类本身(Function)‘class’修改类原型、替换构造函数实例计数、禁止new调用无 / 新类 / 新构造函数
方法装饰器方法本身(Function)‘method’改写方法、添加日志、延迟执行方法调用日志、this绑定无 / 新方法
属性装饰器undefined(无意义)‘field’修改属性初始值初始化日志、值翻倍无 / 初始化函数(参数为初始值,返回最终值)
getter 装饰器getter函数(Function)‘getter’改写getter、缓存结果缓存开销大的计算结果无 / 新getter
setter 装饰器setter函数(Function)‘setter’改写setter、添加值校验限制属性赋值范围无 / 新setter
accessor 装饰器含get/set的对象‘accessor’改写自动生成的get/set为accessor添加日志无 / 新get/set/init函数

各类型示例(关键场景)

  1. 类装饰器:实例计数

    function countInstances(value: any) {let count = 0;return class extends value { // 返回新子类constructor(...args: any[]) {super(...args);count++;this.count = count;}};
    }
    @countInstances
    class MyClass {}
    new MyClass().count; // 1
    
  2. 方法装饰器:延迟执行

    function delay(ms: number) {return (value: any) => {return function (...args: any[]) {setTimeout(() => value.apply(this, args), ms);};};
    }
    class Logger {@delay(1000) // 延迟1秒执行log(msg: string) { console.log(msg); }
    }
    
  3. 属性装饰器:初始值翻倍

    function twice() {return (initialValue: any) => initialValue * 2; // 返回初始化函数
    }
    class C {@twice field = 3; // 最终值为6
    }
    

四、装饰器执行顺序

装饰器执行分评估阶段应用阶段,顺序严格且影响功能正确性:

  1. 评估阶段(Evaluation)

    • 定义:计算@后表达式的值,得到装饰器函数(本质是“获取装饰器”)。
    • 顺序:按装饰器出现的先后顺序评估,类装饰器先于类内部装饰器;若成员名是计算值(如[Symbol.iterator]()),则在对应装饰器评估后计算。
    • 示例:@d('类')@d('静态属性')@d('方法')(评估顺序)。
  2. 应用阶段(Application)

    • 定义:将评估得到的装饰器函数应用于被装饰对象(本质是“执行装饰器”)。
    • 顺序:
      1. 原型方法装饰器 → 2. 静态属性/方法装饰器 → 3. 实例属性装饰器 → 4. 类装饰器;
      2. 若一个成员有多个装饰器(如@bound @log greet()),则内层装饰器先执行@log先应用,@bound后应用于@log的结果)。
  3. 执行顺序示例输出
    参考网页示例,执行顺序如下(关键步骤):

    评估 @d(): 类装饰器 → 评估 @d(): 静态属性装饰器 → 评估 @d(): 方法装饰器 → 计算方法名 → 评估 @d(): 实例属性装饰器  
    应用 @d(): 方法装饰器 → 应用 @d(): 静态属性装饰器 → 应用 @d(): 实例属性装饰器 → 应用 @d(): 类装饰器  
    

4. 关键问题

问题1:TS 5.0 中装饰器的两种语法(标准 vs 传统)在启用方式、语法特征上有何核心差异?为何官方更推荐标准语法?

答案
两者核心差异如下表,官方推荐标准语法的核心原因是其符合ECMAScript标准,兼容性更强且无需额外编译参数:

对比维度标准语法传统语法
启用方式直接使用,无需编译参数需开启--experimentalDecorators编译参数
语法特征装饰器函数接受valuecontext参数,返回值规则明确装饰器函数参数为target/propertyKey/descriptor(不同装饰器参数不同)
兼容性遵循ECMAScript标准,浏览器/Node.js未来可原生支持TS私有语法,无标准支持,仅TS环境可用
核心优势类型安全(context参数有明确接口如ClassMethodDecoratorContext历史兼容性(适配旧TS项目)

官方推荐标准语法的原因:

  1. 符合ECMAScript语言发展方向,避免依赖TS私有语法导致的迁移成本;
  2. context参数提供更丰富的元信息(如kind/addInitializer),支持更复杂的初始化逻辑;
  3. 无需额外编译参数,降低项目配置复杂度。

问题2:属性装饰器的value参数为何恒为undefined?如何通过属性装饰器修改属性的初始值?

答案

  1. valueundefined的原因
    属性装饰器作用于“类顶部声明的属性(field)”,其执行时机是类定义阶段,此时属性尚未初始化(初始值在实例化时才赋值),因此无法通过value获取属性值,故value恒为undefined

  2. 修改属性初始值的方式
    属性装饰器可返回一个初始化函数,该函数会在属性实例化时自动执行,参数为属性的初始值,返回值即为属性的最终值,流程如下:

    • 步骤1:装饰器返回初始化函数(参数:初始值,返回值:最终值);
    • 步骤2:类实例化时,将属性的初始值传入该函数;
    • 步骤3:函数返回的结果作为属性的最终值。

    示例(初始值翻倍):

    // 装饰器:返回初始化函数,将初始值翻倍
    function double() {return (initialValue: number) => initialValue * 2;
    }
    class C {@double field = 5; // 初始值5 → 初始化函数处理后→最终值10
    }
    new C().field; // 10
    

问题3:当一个类的方法同时有多个装饰器(如@bound @log greet()),执行顺序如何?请结合“评估阶段”和“应用阶段”解释,并说明为何要遵循这一顺序?

答案

  1. 执行顺序
    @bound @log greet()为例,顺序分为两阶段:

    • 评估阶段:按装饰器从左到右的出现顺序评估,即先评估@bound(计算bound表达式得装饰器函数),再评估@log(计算log表达式得装饰器函数);
    • 应用阶段:按装饰器从右到左的顺序应用,即先执行@log(改写greet方法,添加日志逻辑),再执行@bound(对@log改写后的方法进行this绑定)。

    最终效果:greet方法先被添加日志,再绑定this,确保两者功能都生效。

  2. 遵循该顺序的原因
    这一顺序符合“函数组合”的逻辑(内层装饰器先处理目标,外层装饰器再处理内层的结果),确保多个装饰器的功能不冲突且可叠加:

    • 若先执行@bound再执行@log,则@log会改写@bound绑定后的方法,可能导致this绑定失效;
    • 按“评估左→右,应用右→左”的顺序,可保证每个装饰器都作用于前一个装饰器的处理结果,实现功能叠加(如“日志+this绑定”同时生效)。
http://www.dtcms.com/a/528528.html

相关文章:

  • 一、基础预训练模型与能力
  • 上海网站建设选缘魁-企查公司简介模板文案
  • 重磅新书 | 《链改2.0:从数字资产到RWA》
  • 【IOS开发】SwiftUI + OpenCV实现图片的简单处理(一)
  • 【Docker】docker run
  • 成都网站建设 Vr便民网
  • LLama3架构原理浅浅学学
  • docker存储管理
  • Transformer架构发展历史
  • 【AI原生架构:数据架构】9、从打破数据孤岛到价值升维,企业数据资产变现全流程
  • Kubernetes 上的 GitLab + ArgoCD 实践(二):使用自建 GitLab Runner 完善 CI 流程
  • 网站如何查看浏览量2008建设网站
  • 开学季技术指南:高效知识梳理与实战经验分享
  • 网站推广计划渠道国外做美食视频网站有哪些
  • 金蝶K3老单 工艺路线维护特殊字符(使用模块返回值的方法)
  • 信贷控制范围
  • 乐陵网站优化最简单的网站设计
  • 项目信息和生产安全管理指南(试行)
  • 【Tesla】ICCV 2025技术分享
  • 企业做网站营销企业网站 响应式
  • 深度学习C++中的数据结构:栈和队列
  • 2025-tomcat web实践
  • 免费建立微信网站如何设计的英文网站
  • liferay 做网站哪里有网站开发公司
  • Leetcode 38
  • Django 学习路线图
  • 把网站放到服务器公司做网站需要准备什么资料
  • 如何批量获取蛋白质序列的所有结构域(domain)数据-2
  • MySQL基础知识大全
  • 站群服务器都有什么作用