装饰器是JavaScript中用于动态修改类及其成员行为的函数,通过@语法应用,支持类、方法、属性等层面的元编程操作,常用于自动注册、权限控制、数据校验和AOP等场景,结合TypeScript或Babel已在Angular、NestJS等框架中广泛使用,其核心在于操控目标对象的描述符以实现非侵入式增强。
装饰器与元编程是JavaScript ES7中非常强大的特性,它们让开发者可以在类、方法、属性等层面动态地修改行为。虽然装饰器目前还处于提案阶段(Stage 3),但在TypeScript和Babel的支持下,已经广泛应用于实际项目中,尤其是在Angular、NestJS等框架里。
装饰器本质上是一个函数,用来“标记”或“增强”类及其成员。它通过@expression语法应用,其中expression必须是一个在运行时可调用的函数。
装饰器可以用于:
例如:
function readonly(target, name, descriptor) { descriptor.writable = false; return descriptor; } class Person { @readonly name = 'Alice'; }
装饰器函数接收不同的参数,取决于它被应用的位置:
以方法装饰器为例:
function log(target, name, descriptor) {
const original = descriptor.value;
descriptor.value = function (...args) {
console.log(`Calling "${name}" with`, args);
return original.apply(this, args);
};
return descriptor;
}
class Calculator {
@log
add(a, b) {
return a + b;
}
}
这样每次调用add方法时都会自动打印日志,无需修改原逻辑。
元编程指的是“编写操作程序的程序”,装饰器正是实现元编程的重要手段。
常见用途包括:
我们可以创建一个装饰器,在赋值时检查类型是否匹配:
function typeCheck(expectedType) {
return function (target, propertyKey) {
let value;
const getter = function () {
return value;
};
const setter = function (newValue) {
if (typeof newValue !== expectedType) {
throw new TypeError(`${propertyKey} must be a ${expectedType}`);
}
value = newValue;
};
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
};
}
class User {
@typeCheck('string')
name;
@typeCheck('number')
age;
}
const user = new User();
user.name = 'Bob'; // 正常
user.age = '25'; // 抛错:age must be a number
基本上就这些。装饰器虽未正式纳入ES标准,但结合Babel或TypeScript使用已相当成熟。掌握它能显著提升代码的抽象能力和可维护性,特别是在构建框架或大型应用时。关键是理解其执行时机和目标对象的操控方式,灵活运用于解耦和增强逻辑。不复杂但容易忽略细节,比如descriptor的返回和属性描述符的配置。