装饰器是 TS 的一种特殊语法,本质是给类、属性、方法、参数添加注解的函数,目前是 ES 提案,需要开启配置才能使用。它的核心作用是不修改原有代码逻辑,就能为代码扩展功能、添加标识。 工作原理很简单:编译时执行,TS 编译器会在编译阶段,将装饰器函数应用到目标代码上,对目标进行包装、修改或扩展。它不会改变运行时的核心逻辑,只是在代码编译期做一层拦截和增强,属于元编程的核心语法,用极简的注解方式,实现代码的无侵入式扩展。
装饰器工厂就是返回装饰器函数的函数,专门解决普通装饰器无法传参的问题。普通装饰器不能接收自定义参数,而工厂函数可以先接收参数,再返回标准装饰器,灵活性大幅提升。 使用方式特别简单:先定义工厂函数,内部 return 装饰器;使用时直接带参数调用,比如@Log('请求')。它能让同一个装饰器,根据传入的不同参数,实现不同的增强效果,比如自定义日志标识、校验规则、配置项等,是装饰器最常用的写法,完美适配多样化的业务场景。
元编程就是操作代码本身的编程方式,装饰器是 TS 实现元编程的最佳工具。不用修改业务核心代码,只需通过注解,就能在编译时动态修改类的结构、添加属性、劫持方法执行、绑定配置信息。 比如给类自动添加方法、给函数劫持执行前后逻辑、给属性绑定校验规则。装饰器可以直接访问目标代码的结构,动态扩展或修改它的行为,让代码具备自我描述、自我修改的能力。这种方式无侵入、低耦合,让通用逻辑和业务逻辑彻底分离,是框架和工具库的核心实现手段。
装饰器本身不能存储数据,配合reflect-metadata库,就能实现元数据的挂载与反射读取。在装饰器中,调用库的 API,把类型信息、配置、标识等元数据,附加到类、方法、属性上。 运行时可以通过反射 API,读取这些编译时存储的元数据,获取目标的附加信息。比如给接口方法标记路由地址,给服务标记依赖类型,这些信息都能通过反射精准获取。它让装饰器从单纯的代码增强,变成了可感知、可读取的信息载体,是实现高级框架功能的基础。
两者是相辅相成、缺一不可的搭档关系。装饰器是载体,负责在编译时定位到目标代码,执行元数据的挂载操作;反射元数据是工具,提供存储和读取元数据的 API,负责保存信息并支持运行时获取。 没有装饰器,元数据无法便捷地附加到代码上;没有反射元数据,装饰器只能做逻辑增强,无法存储和传递信息。它们配合实现了 TS 的元信息管理,是依赖注入、路由自动注册、校验框架等核心功能的底层支撑,共同构建了 TS 的高级元编程能力。
装饰器模式在实际开发中用途极广,是简化重复代码的神器。最常用的场景:日志记录,自动打印方法入参、出参、执行时间;请求处理,统一给接口加加载、异常捕获;数据校验,给属性自动绑定校验规则;路由注册,给控制器自动绑定接口地址。 像 NestJS、Midway 这些服务端框架,几乎全程依赖装饰器。它能把日志、缓存、权限、埋点这些通用横切逻辑,全部抽成装饰器,业务代码只关注核心逻辑,代码量大幅减少,可读性和维护性直接拉满。
AOP 的核心是把通用横切逻辑从业务代码中分离,装饰器是 TS 实现 AOP 的最佳方案。通过装饰器劫持目标方法的执行,在方法调用前、后、异常时,插入通用逻辑,完全不侵入业务代码。 比如给方法加装饰器,自动在执行前打印日志,执行后计算耗时,报错时捕获异常。这些日志、计时、异常处理就是切面,装饰器把切面和业务逻辑解耦,不用在每个方法里手写重复代码。一行注解就能实现 AOP,让代码更简洁,通用逻辑集中管理,修改时一处改、处处生效。
依赖注入(DI)是自动创建实例、管理依赖的设计模式,装饰器 + 反射元数据是它的核心实现。首先用@Injectable()装饰器标记服务类,告诉容器这个类可被注入;然后在需要使用的地方,用@Inject()或构造函数注入依赖。 装饰器会把类的依赖信息通过元数据存储起来,IOC 容器运行时通过反射读取这些信息,自动创建实例并注入,无需手动 new 对象。这是 NestJS、Angular 框架的核心功能,彻底消除硬编码依赖,降低耦合度,让代码更易测试、更易扩展。