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 已支持装饰器(实验性)

七、课后作业

  1. 实现一个 @readonly 装饰器,让方法不能被修改
  2. 实现一个 @deprecated 装饰器,调用方法时打印警告
  3. new.target 实现一个抽象基类,不能直接实例化

有问题欢迎评论区留言,大家一起讨论!


标签:JavaScript | 装饰器 | 元属性 | Stage3 | 类装饰器 | 方法装饰器 | 前端进阶

更多推荐