JavaScript进阶⑤|装饰器与元属性,未来JavaScript特性
·
author: 专注前端开发,分享JavaScript干货
title: JavaScript进阶⑤|装饰器与元属性,未来JavaScript特性
update: 2026-04-28
tags: JavaScript,装饰器,元属性,Stage3,类装饰器,方法装饰器,前端进阶
作者:专注前端开发,分享JavaScript干货
更新时间:2026年4月
适合人群:掌握ES6+,想了解前沿JS特性的开发者
前言:装饰器是什么?
装饰器(Decorator) 是一种特殊类型的声明,可以附加到类、方法、访问器、属性或参数上,用于修改其行为。
装饰器目前处于 Stage 3 阶段(接近正式标准),但 TypeScript 和许多框架(如 Angular、NestJS)已经在广泛使用。
一、装饰器基础(当前Stage 3提案)
1.1 类装饰器
// 类装饰器:接收类本身
function logged(constructor) {
console.log(`类 ${constructor.name} 被定义`);
// 可以返回一个新的类替换原类
return class extends constructor {
createdAt = new Date();
};
}
class User {
constructor(name) {
this.name = name;
}
}
// 手动应用装饰器(模拟)
const DecoratedUser = logged(User);
const user = new DecoratedUser("张三");
console.log(user.createdAt); // 当前时间
1.2 方法装饰器
// 方法装饰器:接收目标原型、方法名、属性描述符
function log(target, propertyKey, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args) {
console.log(`调用方法 ${propertyKey},参数:`, args);
const result = originalMethod.apply(this, args);
console.log(`方法 ${propertyKey} 返回:`, result);
return result;
};
return descriptor;
}
1.3 手动应用装饰器
class Calculator {
add(a, b) {
return a + b;
}
}
// 手动应用装饰器
const desc = Object.getOwnPropertyDescriptor(Calculator.prototype, "add");
log(Calculator.prototype, "add", desc);
Object.defineProperty(Calculator.prototype, "add", desc);
const calc = new Calculator();
calc.add(1, 2); // 打印日志,返回 3
二、属性装饰器
// 属性装饰器
function required(target, propertyKey) {
console.log(`${propertyKey} 是必填字段`);
}
// 参数装饰器
function minLength(target, methodName, parameterIndex) {
console.log(`参数 ${parameterIndex} 长度至少 3`);
}
class User {
@required
name = "";
greet(@minLength message) {
console.log(message);
}
}
三、元属性(new.target)
// new.target:指向被 new 调用的构造函数
function Person(name) {
if (!new.target) {
throw new Error("必须使用 new 调用 Person");
}
this.name = name;
}
const p1 = new Person("张三"); // ✅
const p2 = Person("李四"); // ❌ 报错
// 在类中使用
class Animal {
constructor(name) {
if (new.target === Animal) {
throw new Error("Animal 是抽象类,不能直接实例化");
}
this.name = name;
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
}
const dog = new Dog("旺财", "金毛"); // ✅
四、实战:用装饰器实现重试机制
// 装饰器:方法调用失败时自动重试
function retry(times = 3, delay = 1000) {
return function(target, propertyKey, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = async function(...args) {
let lastError;
for (let i = 0; i < times; i++) {
try {
return await originalMethod.apply(this, args);
} catch (error) {
lastError = error;
console.log(`第 ${i + 1} 次失败,${delay}ms 后重试...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError;
};
return descriptor;
};
}
// 模拟不稳定的网络请求
class ApiService {
@retry(3, 500)
async fetchData() {
if (Math.random() > 0.7) {
return { data: "成功" };
}
throw new Error("网络错误");
}
}
// 手动应用版本
class ApiServiceManual {
async fetchData() {
if (Math.random() > 0.7) {
return { data: "成功" };
}
throw new Error("网络错误");
}
}
const desc2 = Object.getOwnPropertyDescriptor(ApiServiceManual.prototype, "fetchData");
retry(3, 500)(ApiServiceManual.prototype, "fetchData", desc2);
Object.defineProperty(ApiServiceManual.prototype, "fetchData", desc2);
五、实战:用装饰器实现性能测量
// 装饰器:测量方法执行时间
function measure(target, propertyKey, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args) {
const start = performance.now();
const result = originalMethod.apply(this, args);
const end = performance.now();
console.log(`方法 ${propertyKey} 执行时间:${(end - start).toFixed(2)}ms`);
return result;
};
return descriptor;
}
class DataProcessor {
@measure
process(data) {
const start = Date.now();
while (Date.now() - start < 100) {}
return data.map(x => x * 2);
}
}
// 手动应用版本
class DataProcessorManual {
process(data) {
const start = Date.now();
while (Date.now() - start < 100) {}
return data.map(x => x * 2);
}
}
const desc3 = Object.getOwnPropertyDescriptor(DataProcessorManual.prototype, "process");
measure(DataProcessorManual.prototype, "process", desc3);
Object.defineProperty(DataProcessorManual.prototype, "process", desc3);
const processor = new DataProcessorManual();
processor.process([1, 2, 3]); // 打印执行时间
六、知识卡
| 概念 | 说明 |
|---|---|
| 装饰器 | 修改类/方法行为的声明 |
| 类装饰器 | 接收构造函数 |
| 方法装饰器 | 接收原型、方法名、描述符 |
| 属性装饰器 | 接收目标和属性名 |
new.target |
指向被 new 调用的构造函数 |
| Stage 3 | 接近正式标准 |
| TypeScript | 已支持装饰器(实验性) |
七、课后作业
- 实现一个
@readonly装饰器,让方法不能被修改 - 实现一个
@deprecated装饰器,调用方法时打印警告 - 用
new.target实现一个抽象基类,不能直接实例化
有问题欢迎评论区留言,大家一起讨论!
标签:JavaScript | 装饰器 | 元属性 | Stage3 | 类装饰器 | 方法装饰器 | 前端进阶
更多推荐


所有评论(0)