TypeScript 装饰器执行顺序
在前端开发中,不少人在接触 TypeScript 后,都会被它的“装饰器”功能吸引——这个能给类、方法、属性“穿外套”的工具,能让代码变得更简洁,也能实现不少有趣的功能,比如给方法加日志、给类加权限校验。但很多人用的时候,会遇到一个困惑:明明在不同地方写了装饰器,为啥执行顺序和自己想的不一样?
先举个生活化的例子,你就懂了。假设你要出门,需要做三件事:先穿袜子,再穿裤子,最后穿鞋子。这个顺序是固定的,要是先穿鞋子再穿袜子,那肯定穿不上。装饰器的执行顺序,就和“穿衣服”的逻辑很像,核心是“从外到内、从下到上”的嵌套规则,对应到代码里,得先搞清楚装饰器的类型——毕竟给类、给方法、给属性加的装饰器,执行逻辑还有点不一样。

方法装饰器:先入内层,后外层
先看最常用的方法装饰器,比如你给一个“点餐方法”加两个装饰器:一个是记录“点餐时长”的计时装饰器,另一个是校验“是否有点餐权限”的权限装饰器。代码里写的时候,可能会这么排:
`typescript
class Cafe {
@CheckPermission() // 权限装饰器
@LogOrderTime() // 计时装饰器
orderDish() {
console.log("出餐!");
}
}
`
这里的执行顺序可不是“先权限、后计时”,而是反过来:先执行最靠近方法的@LogOrderTime(内层),再执行外面的@CheckPermission(外层)。就像你先给脚套袜子,再套裤腿——穿的时候先套袜子(内层),但脱的时候会先脱裤子(外层),装饰器也是类似的“反向外层”逻辑:内层装饰器先执行“前置逻辑”,外层后执行;但“后置逻辑”是外层先执行,内层后执行。
类装饰器:先做内部,后做外部
类装饰器的逻辑和方法类似,只是作用对象从方法变成了整个类。比如给类加两个装饰器:一个是给类自动加“创建时间”的AddCreateTime,另一个是打印“类初始化成功”的LogClassInit。写的时候如果这么排:
`typescript
@LogClassInit() // 外层装饰器
@AddCreateTime() // 内层装饰器
class Cafe {}
`
执行顺序还是“先内层、后外层”:先给类加创建时间(AddCreateTime的前置逻辑),再打印初始化成功(LogClassInit的前置逻辑)。后置逻辑则是反过来:先执行LogClassInit的收尾,再执行AddCreateTime的收尾,就像先给类“贴创建时间标签”(内层),再“喊一句类准备好了”(外层),喊完之后才会整理标签,整个收尾是从外到内的。
多个同类型装饰器:从左到右
要是同一个地方加了多个同类型的装饰器,比如给同一个方法加两个日志装饰器@LogA和@LogB,代码写成@LogA() @LogB() orderDish(),那执行顺序就是“从左到右”的外层先执行。就像你同时戴两个手环,左手先戴的A,右手后戴的B——穿的时候A在最外层,执行前置逻辑先出A;脱的时候B在最外层,后置逻辑先出B,对应代码里就是LogA的前置先跑,LogB的前置后跑,后置时LogB先收尾,LogA后收尾。
最后说个容易踩的坑:装饰器的执行顺序和“代码书写顺序”是反向的,除了多个同类型的情况。比如写的时候越靠近目标(方法/类)的装饰器,执行前置逻辑越早,后置逻辑越晚。搞懂这个逻辑,下次用装饰器写权限、日志的时候,就不会因为顺序错了导致功能失灵。