作为一名前端开发者,你一定遇到过这样的场景:
想给类加一个日志功能,却要每个方法都写console.log();
想验证参数是否合法,却要在每个方法里重复判断。
TypeScript的装饰器就像给代码贴"魔法贴纸",能优雅地解决这些问题。本文将带你从零掌握装饰器的5种类型,附带真实案例,看完就能直接用。
一、类装饰器:给整个类加功能
1.1 参数说明
javascript
function 类装饰器(target: Function): void | Function
target:当前类的构造函数(相当于类的身份证)
1.2 实战案例:记录类创建时间
javascript
// 创建装饰器
function 日志类(target: Function) {
target.prototype.创建时间 = new Date().toLocaleString();
}
// 使用装饰器
@日志类
class 用户 {
获取创建时间() {
return this.创建时间;
}
}
const user = new 用户();
console.log(user.获取创建时间()); // 输出类似 "2025-10-28 15:30:00"
💡 通过修改类的原型,我们给所有实例免费加了
创建时间属性。
二、方法装饰器:修改方法行为
2.1 参数说明
typescript
function 方法装饰器(
target: Object, // 类的原型
methodName: string, // 方法名
descriptor: PropertyDescriptor // 方法配置
): void | PropertyDescriptor
2.2 实战案例:给方法加权限校验
typescript
function 需要登录(target: any, methodName: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args) {
if (!this.用户是否登录) {
throw new Error("请先登录");
}
return originalMethod.apply(this, args);
};
return descriptor;
}
class 服务 {
用户是否登录 = false;
@需要登录
获取敏感数据() {
return "机密信息";
}
}
const service = new 服务();
service.获取敏感数据(); // 报错:请先登录
🛡️ 通过包装原方法,我们实现了权限控制而无需修改业务逻辑。
三、属性装饰器:标记或初始化属性
3.1 参数说明
typescript
function 属性装饰器(
target: Object, // 类的原型
propertyName: string // 属性名
): void
3.2 实战案例:标记必填字段
typescript
function 必填(target: any, propertyName: string) {
const descriptor = {
get() {
throw new Error(`${propertyName} 是必填字段`);
},
set(value: any) {
if (value === undefined) {
throw new Error(`${propertyName} 不能为空`);
}
target[propertyName] = value;
}
};
Object.defineProperty(target, propertyName, descriptor);
}
class 表单 {
@必填
用户名!: string;
}
const form = new 表单();
form.用户名 = "张三"; // 正常
console.log(form.用户名); // 输出 "张三"
form.用户名 = undefined; // 报错:用户名 不能为空
✅ 通过重写getter/setter,我们实现了字段校验。
四、参数装饰器:记录参数信息
4.1 参数说明
typescript
function 参数装饰器(
target: Object, // 类的原型
methodName: string | undefined, // 方法名(构造函数为undefined)
parameterIndex: number // 参数位置(从0开始)
): void
4.2 实战案例:记录参数来源
typescript
function 参数追踪(target: any, methodName: string, parameterIndex: number) {
console.log(`方法 ${methodName} 的第 ${parameterIndex} 个参数被调用`);
}
class 计算器 {
加法(@参数追踪 a: number, @参数追踪 b: number) {
return a + b;
}
}
const calc = new 计算器();
calc.加法(10, 20); // 控制台输出:
// 方法 加法 的第 0 个参数被调用
// 方法 加法 的第 1 个参数被调用
📊 这个功能在调试时特别有用,能快速定位参数传递路径。
五、访问器装饰器:控制属性访问
5.1 参数说明
typescript
function 访问器装饰器(
target: Object, // 类的原型
name: string, // 属性名
descriptor: PropertyDescriptor // 访问器配置
): void | PropertyDescriptor
5.2 实战案例:缓存get方法结果
ini
function 缓存(target: any, name: string, descriptor: PropertyDescriptor) {
const originalGetter = descriptor.get;
const cacheKey = `$$${name}Cache`;
descriptor.get = function() {
if (this[cacheKey] === undefined) {
this[cacheKey] = originalGetter.call(this);
}
return this[cacheKey];
};
return descriptor;
}
class 费波那契 {
private n = 10;
@缓存
get 第n项() {
// 模拟耗时计算
let result = 0, a = 1, b = 1;
for (let i = 3; i <= this.n; i++) {
result = a + b;
a = b;
b = result;
}
return b;
}
}
const fib = new 费波那契();
console.log(fib.第n项); // 第一次计算
console.log(fib.第n项); // 直接返回缓存结果
⚡ 通过缓存getter结果,避免重复计算。
六、使用装饰器的注意事项
-
启用配置 :在
tsconfig.json中添加json{ "compilerOptions": { "experimentalDecorators": true, "emitDecoratorMetadata": true } } -
执行顺序 :多个装饰器按从下往上执行
swift@装饰器A @装饰器B class 示例 {} // 实际执行顺序是:装饰器B → 装饰器A -
参数装饰器限制 :不能用于
getter/setter的参数
七、装饰器应用场景
| 场景 | 推荐装饰器类型 |
|---|---|
| 权限控制 | 方法装饰器 |
| 参数校验 | 参数装饰器 |
| 缓存结果 | 方法/访问器装饰器 |
| 字段验证 | 属性装饰器 |
| 日志记录 | 类/方法装饰器 |
八、总结
装饰器就像给代码贴"功能补丁",能让你:
- 解耦业务逻辑:把日志、权限等公共逻辑抽离
- 提升代码可维护性:修改装饰器就能影响所有相关代码
- 实现AOP编程:在不修改原代码的情况下增强功能
建议从简单场景开始尝试,比如先用方法装饰器加日志,逐步探索更复杂的用法。掌握装饰器后,你会发现很多重复代码都可以优雅地消失了!