在javascript中实现装饰器主要有两种方式:一是使用高阶函数,二是采用es7+的装饰器语法(@decorator)。高阶函数通过接收原函数并返回增强后的新函数,可在不修改原函数的前提下添加日志、缓存、性能监控等横切功能,兼容性好且无需转译,适用于函数级别的装饰;而es7+装饰器语法更具声明性,支持类和类成员的装饰,通过@符号直接标注,可实现类的密封、方法只读、废弃提示、权限校验、数据验证、依赖注入和路由定义等高级元编程能力,但需借助babel等工具转译,目前仍处于提案阶段。两种方式均遵循开闭原则,实现关注点分离、代码复用与可维护性提升,其中高阶函数适用于通用函数增强,es7+装饰器更适合类结构化应用与框架开发,开发者可根据项目需求和技术栈选择合适方案。

在JavaScript中实现装饰器,主要有两种途径:一种是利用高阶函数(Higher-Order Functions)来包装现有函数或类,这是装饰器模式在JS中的经典实现;另一种是借助ES7+提案中的装饰器语法(
@decorator
在JavaScript中,实现装饰器模式,我们通常会选择以下两种方式,这取决于你当前项目的技术栈和对未来语言特性的采纳程度。
1. 基于高阶函数的实现(函数装饰器)
这是最通用、兼容性最好的方式。一个高阶函数接收一个函数作为参数,并返回一个新的函数,这个新函数在执行时会包含原函数的功能,并附加了额外的逻辑。
// 简单的日志装饰器
function logDecorator(func) {
return function(...args) {
console.log(`Calling function: ${func.name || 'anonymous'} with args:`, args);
const result = func.apply(this, args); // 保持this上下文
console.log(`Function ${func.name || 'anonymous'} returned:`, result);
return result;
};
}
// 简单的缓存装饰器
function memoizeDecorator(func) {
const cache = {};
return function(...args) {
const key = JSON.stringify(args); // 简单的缓存键生成
if (cache[key]) {
console.log(`Cache hit for ${func.name || 'anonymous'} with args:`, args);
return cache[key];
}
console.log(`Cache miss for ${func.name || 'anonymous'} with args:`, args);
const result = func.apply(this, args);
cache[key] = result;
return result;
};
}
// 示例应用
function add(a, b) {
return a + b;
}
const loggedAdd = logDecorator(add);
loggedAdd(1, 2); // 会输出日志
const memoizedAdd = memoizeDecorator(add);
memoizedAdd(3, 4); // 第一次计算并缓存
memoizedAdd(3, 4); // 第二次直接从缓存获取2. 基于ES7+提案的装饰器语法(类装饰器与方法装饰器)
这种方式更接近其他语言(如Python、Java)的装饰器语法,通过
@
类装饰器:
一个类装饰器接收构造函数作为参数,并可以返回一个新的构造函数,或者修改原构造函数。
// @sealed 装饰器:让类不可扩展,属性不可配置
function sealed(constructor) {
Object.seal(constructor);
Object.seal(constructor.prototype);
return constructor; // 可以选择返回原构造函数或新的构造函数
}
// @logClass 装饰器:打印类被创建时的信息
function logClass(constructor) {
console.log(`Class ${constructor.name} was defined.`);
return class extends constructor {
constructor(...args) {
super(...args);
console.log(`Instance of ${constructor.name} created.`);
}
};
}
@sealed
@logClass // 多个装饰器会从下往上执行,但实际应用时,类装饰器通常是修改或替换类本身
class MyClass {
constructor(name) {
this.name = name;
}
greet() {
return `Hello, ${this.name}!`;
}
}
const instance = new MyClass('World');
console.log(instance.greet());
// 尝试修改或扩展 MyClass 会失败
try {
MyClass.prototype.newMethod = function() {};
} catch (e) {
console.error("Cannot add new method to sealed class prototype:", e.message);
}方法装饰器:
方法装饰器接收三个参数:
target
key
descriptor
descriptor.value
// @readonly 装饰器:让方法变为只读(不可修改、不可删除)
function readonly(target, key, descriptor) {
descriptor.writable = false;
return descriptor;
}
// @deprecate 装饰器:标记方法已废弃
function deprecate(message) {
return function(target, key, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args) {
console.warn(`Warning: Method "${key}" is deprecated. ${message}`);
return originalMethod.apply(this, args);
};
return descriptor;
};
}
class User {
constructor(name) {
this.name = name;
}
@readonly
getName() {
return this.name;
}
@deprecate('Please use getName() instead.')
oldGetName() {
return this.name;
}
}
const user = new User('Alice');
console.log(user.getName());
user.oldGetName(); // 会触发废弃警告
// 尝试修改 getName 方法会失败
try {
user.getName = function() { return 'New Name'; };
} catch (e) {
console.error("Cannot reassign readonly method:", e.message);
}思考一下,我们写代码时总会遇到一些横切关注点(Cross-cutting Concerns),比如日志记录、性能监控、权限校验、数据验证、事务管理等等。这些功能往往散落在应用程序的各个模块中,如果直接把它们的代码写到业务逻辑里,那业务代码就会变得臃肿、难以维护。
装饰器模式,本质上就是提供了一种优雅的方案来处理这些横切关注点。它允许你在不修改原有对象(或函数)代码的前提下,动态地给它们“穿上”一层新的功能外衣。这就像给一个普通杯子加上保温套、防滑垫一样,杯子本身还是那个杯子,但它现在有了额外的功能。
这种模式的好处显而易见:
我个人觉得,当你发现自己的函数或类开始变得“胖”起来,里面塞满了各种与核心职责无关的辅助性代码时,就应该考虑引入装饰器了。它能让你的代码看起来更整洁,也更符合面向对象的设计原则。
高阶函数实现装饰器,其实是JavaScript函数式编程思想的一种体现。它非常灵活,不需要任何特殊的语法支持,因此在任何JS环境中都可以使用。
核心原理很简单:一个函数(我们称之为“装饰器函数”)接收另一个函数作为参数,然后返回一个新的函数。这个新返回的函数在内部调用原始函数,并在其前后或者特定位置插入额外的逻辑。
举个例子,假设我们有一个计算阶乘的函数:
function factorial(n) {
if (n === 0 || n === 1) {
return 1;
}
return n * factorial(n - 1);
}现在,我们想知道这个函数每次执行花了多少时间。我们不希望直接修改
factorial
// 性能监控装饰器
function measurePerformance(originalFunc) {
return function(...args) { // 返回一个新的函数
const start = performance.now(); // 记录开始时间
const result = originalFunc.apply(this, args); // 调用原始函数,并保持this上下文
const end = performance.now(); // 记录结束时间
console.log(`${originalFunc.name || 'anonymous'} took ${end - start} ms to execute.`);
return result; // 返回原始函数的执行结果
};
}
// 应用装饰器
const measuredFactorial = measurePerformance(factorial);
// 调用被装饰的函数
console.log(measuredFactorial(5));
console.log(measuredFactorial(10));这里的
measurePerformance
factorial
measuredFactorial
measuredFactorial
measurePerformance
factorial
这种方式的优点在于它的纯粹性和通用性。你可以很方便地组合多个高阶函数装饰器,形成一个功能链。比如,你可以在
measurePerformance
logDecorator
当然,也有一些小“缺点”:它不如ES7+的
@
ES7+(目前仍是Stage 3提案)引入的装饰器语法,用一个
@
特点:
@decoratorName
应用场景:
ES7+装饰器在实际开发中有着广泛的应用,尤其是在框架和库的开发中,它们能极大地简化代码并提升可读性。
日志记录与性能监控: 这是最常见的应用。你可以创建一个
@log
@measure
// 假设这是我们的装饰器实现
// function log(target, key, descriptor) { /* ... */ }
// function measure(target, key, descriptor) { /* ... */ }
class Calculator {
@log
@measure
add(a, b) {
return a + b;
}
}
const calc = new Calculator();
calc.add(1, 2); // 自动打印日志和性能数据权限控制与认证: 在Web应用中,经常需要检查用户是否有权限访问某个方法或资源。
@authRequired
@hasRole('admin')// function authRequired(target, key, descriptor) { /* ... */ }
class AdminPanel {
@authRequired
deleteUser(userId) {
console.log(`Deleting user: ${userId}`);
}
}数据验证: 在处理表单输入或API请求时,往往需要对数据进行严格的验证。
@validate(schema)
// function validate(schema) { return function(target, key, descriptor) { /* ... */ }; }
class UserService {
@validate({ name: String, age: Number })
createUser(data) {
console.log('Creating user:', data);
}
}依赖注入: 某些框架(如Angular)使用装饰器来声明类所需的依赖,框架在创建实例时会自动注入这些依赖。例如
@Injectable()
@Inject(TOKEN)
路由定义: 在一些后端框架(如NestJS)中,装饰器被用来定义HTTP请求的路由和方法。
@Get('/users')@Post()
状态管理: 在某些UI库或框架中,装饰器可以用来标记可观察对象或响应式属性,以便在它们改变时自动更新UI。
ES7+装饰器为JavaScript带来了更强的表现力和更清晰的代码结构,尤其适合那些需要大量元编程和横切关注点处理的复杂应用。然而,由于它仍是提案,生产环境中使用需要注意转译工具的配置和潜在的未来语法变动。
以上就是JS如何实现装饰器?装饰器模式应用的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号