一、什么是装饰器?
装饰器就是普通函数 ,用 @装饰器名 贴在类、方法、属性上,作用就是给被装饰的东西加额外功能,仅此而已。
二、最基础用法:类装饰器
直接贴在类上,函数唯一参数是被装饰类的构造函数,最简单的例子:给类加静态属性。
typescript
// 定义装饰器函数:给类加版本号
function addVersion(target) {
target.version = '1.0.0'; // 给类挂载静态属性
}
// 用@贴在类上,完成装饰
@addVersion
class MyClass {}
// 直接使用装饰器加的属性
console.log(MyClass.version); // 输出:1.0.0
核心逻辑:@addVersion 等价于 addVersion(MyClass),把类传给装饰器函数就行。
三、最常用用法:方法装饰器
贴在类的方法上,日常用得最多,函数有 3 个固定参数:不用死记含义,跟着案例用就会。
简单案例1:给方法加日志
执行方法时,自动打印"方法被调用了",不用在方法里写重复代码。
typescript
// 方法装饰器:打印方法调用日志
function showLog(target, name, descriptor) {
// 保存原方法
const oldFn = descriptor.value;
// 重写方法,加日志逻辑
descriptor.value = function() {
console.log(`【日志】${name}方法被调用了`);
oldFn.apply(this); // 执行原来的方法
};
}
class MyClass {
// 装饰器贴在方法上
@showLog
sayHi() {
console.log('hello TS');
}
}
new MyClass().sayHi();
// 输出:【日志】sayHi方法被调用了 → hello TS
简单案例2:禁止方法被修改
让方法只读,不能被重新赋值,避免误改方法逻辑。
typescript
// 方法装饰器:禁止方法修改
function noChange(target, name, descriptor) {
descriptor.writable = false; // 设为不可写
}
class MyClass {
@noChange
fn() {
console.log('不能改我');
}
}
const obj = new MyClass();
obj.fn = () => {}; // 报错:无法分配到 "fn",因为它是只读属性
四、属性装饰器:给属性加默认值
贴在类的属性上,函数有 2 个参数,最简单的用法:给属性设置默认值,不用在构造函数里初始化。
typescript
// 属性装饰器:设置默认值
function setDefault(val) {
return function(target, name) {
target[name] = val;
};
}
class MyClass {
// 贴在属性上,传默认值
@setDefault('张三')
name;
@setDefault(20)
age;
}
console.log(new MyClass().name); // 输出:张三
console.log(new MyClass().age); // 输出:20
五、装饰器工厂:给装饰器传参
想给装饰器传自定义参数(比如日志加前缀、默认值自定义),就写装饰器工厂------本质是「返回装饰器的函数」,写法超简单,看案例就会。
案例:带前缀的日志装饰器
让日志有自定义前缀,不用写多个装饰器函数。
typescript
// 装饰器工厂:接收自定义前缀
function log(prefix) {
// 返回真正的装饰器函数
return function(target, name, descriptor) {
const oldFn = descriptor.value;
descriptor.value = function() {
console.log(`【${prefix}】${name}方法执行了`);
oldFn.apply(this);
};
};
}
class MyClass {
// 传参使用:自定义前缀"接口请求"
@log('接口请求')
getData() {}
// 传参使用:自定义前缀"操作"
@log('操作')
saveData() {}
}
new MyClass().getData(); // 输出:【接口请求】getData方法执行了
new MyClass().saveData(); // 输出:【操作】saveData方法执行了
六、必记规则:多个装饰器的执行顺序
一个类/方法上贴多个装饰器时,从下往上执行------谁离被装饰的东西近,谁先执行。
typescript
// 定义两个简单装饰器
function A(target) {
console.log('执行装饰器A');
return target;
}
function B(target) {
console.log('执行装饰器B');
return target;
}
// B离类更近,先执行;A后执行
@A
@B
class MyClass {}
// 运行结果:先打印「执行装饰器B」,再打印「执行装饰器A」
等价理解:MyClass = A(B(MyClass)),先执行内层的 B,再执行外层的 A。
七、使用前提:开启TS配置
装饰器是 TS 实验性特性,必须在 tsconfig.json 里加一行配置,否则报错,就加这一个核心项就行:
json
{
"compilerOptions": {
"experimentalDecorators": true, // 必须开启,启用装饰器
"target": "ES5" // 项目基本配置,一般都有
}
}