首页app攻略装饰器模式java js的装饰器

装饰器模式java js的装饰器

圆圆2025-08-14 19:01:39次浏览条评论

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

JS如何实现装饰器?装饰器模式应用

在JavaScript中实现装饰器,主要有两种途径:一种是利用高阶函数(Higher-Order) Functions)来包装已有的函数或类,这是装饰器模式在JS中的经典实现;另一种是借助ES7提议中的装饰器语法(@decorator登录后复制),这种语法除了声明性,但目前仍提出一个提议阶段,需要Babel等工具转译才能使用。核心思想都是在不行为原有代码结构的前提下,为对象或函数添加新的或修改其现有行为。解决方案

在JavaScript中,实现装饰器模式,我们通常会选择以下两种方式,这取决于你当前项目的技术栈和对未来语言特性的采用程度。

1. 基于高阶函数的实现(函数装饰器)

这是最通用、兼容最好的方式。一个高阶函数接收一个函数作为参数,并返回一个新的函数,这个新函数在执行时会包含原函数的功能,并附加了额外的逻辑。

// 简单的日志装饰器function logDecorator(func) { return function(...args) { console.log(`调用函数: ${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 || '匿名'} 与args:`, args); const result = func.apply(this, args); cache[key] = result; return result; };}// 示例应用function add(a, b) { return a b;}constloggingAdd = logDecorator(add);loggedAdd(1, 2); // 会输出日志 const memoizedAdd = memoizeDecorator(add);memoizedAdd(3, 4); //第一次计算并服务器memoizedAdd(3, 4); // 第二次直接从服务器获取记录后复制

2. 基于ES7提议的装饰器语法(类装饰器与方法装饰器)

这种方式更接近其他语言(如Python、Java)的装饰器语法,通过@登录后复制登录后复制登录后复制符号直接评价类或类的方法上。这极大地提升了代码的争议性和声明性,但需要Babel等工具进行转译。

类装饰器:

一类装饰器接收构造函数作为参数,并可以返回一个新的构造函数,或者修改原构造函数。

// @sealed 装饰器:让类不可扩展,属性不可配置 function seal(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(quot;无法向密封类原型添加新方法:quot;, e.message);}登录后复制

方法装饰器:

方法装饰器接收三个参数:target登录后复制(类的原型对象)、key登录后复制(方法名)、descriptor登录后复制(属性参数)。它通常通过descriptor.value登录后复制修改来方法的行为。

// @readonly 装饰器:让方法变得复杂(不可修改、不可删除)function readonly(target, key,descriptor) {descriptor.writable = false; returndescriptor;}// @deprecate 装饰器:标记方法已废弃 function deprecate(message) { return function(target, key,descriptor) { const originMethod =descriptor.value;descriptor.value = function(...args) { console.warn(`警告:方法 quot;${key}quot; is ${message}`); return originalMethod.apply(this, args); }; 返回描述符; };}class User { constructor(name) { this.name = name; } @readonly getName() { return this.name; } @deprecate('请使用 getName() 代替。') 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(quot;无法重新分配只读方法:quot;, e.message);}登录后复制为什么我们需要装饰器模式?

思考一下,我们写代码时俱乐部遇到一些横切关注点(Cross-cutting)这些功能往往散落在应用程序的各个模块中,如果直接把的代码写入到业务逻辑里,那业务代码就会发光、难以维护。

装饰器模式,本质上就是提供了一种优雅的方案来处理这些横切点。它允许你在不修改原有对象(或函数)代码的前提下,动态地给它们“穿上”一层新的功能外衣。这就像给一个普通的杯子加保温套、睡垫一样,杯子本身还是那个杯子,但现在有了额外的功能。

这种模式的好处进一步:分离点:业务修改逻辑保持部分,与非业务功能解耦。代码复用:装饰器本身是可重复用的功能模块,可以应用到多个不同的目标上。可维护性:当需要方便或删除某个横切功能时,只需或删除的装饰器,而产生接触接触业务核心逻辑。开闭原则:对扩展开放,对修改封闭。你很可以地添加新功能(通过添加装饰器),而删除已有的代码。

我个人觉得,当你发现自己的函数或类开始变得“胖”起来,里面塞满了各种与核心职责无关的辅助性代码时,就应该引入装饰器了。它能让你考虑的代码看起来更整洁,也更符合面向对象的设计原则。使用高阶函数实现JavaScript装饰器?

高阶函数实现装饰器,其实是JavaScript函数式如何编程思想的体现。它非常灵活,不需要任何特殊的语法支持,因此在任何JS环境中都可以使用。

核心原理很简单:一个函数(我们称之为“装饰器函数”)接收另一个函数作为参数,返回一个新的函数。然后这个新返回的函数在内部调用原始函数,并在其前后或者特定位置插入外部的逻辑。

举个例子,假设我们有一个计算阶乘的函数:function阶乘(n) { if (n === 0 || n === 1) { return 1; } 返回 n * 阶乘(n - 1);}登录后复制

现在,我们想知道这个函数每次执行需要多少时间。我们不希望直接修改阶乘登录后复制登录后复制登录后复制函数,因为这样会污染它的核心逻辑。其次,一个高阶函数就能派上用场://性能监控装饰器functionmeasurePerformance(originalFunc) { return function(...args) { //返回一个新的函数 const start = Performance.now(); //记录开始时间 const result = originalFunc.apply(this, args); // 调用原始函数,并保持这个上下文 const end = Performance.now(); // 记录结束时间 console.log(`${originalFunc.name || 'anonymous'} take ${end - start} ms toexecute.`); return result; // 返回原始函数的执行结果 };}// 应用装饰器 constmeasuredFactorial =measurePerformance(factorial);// 调用被装饰的函数console.log(measuredFactorial(5));console.log(measuredFactorial(10));登录后复制

这里的measurePerformance登录后复制登录后复制登录后复制就是一个高阶函数实现的装饰器。它接收factorial登录后复制登录后复制登录后复制作为参数,返回了一个新的函数measuredFactori当我们调用measuredFactorial登录后复制登录后复制时,实际上是执行了measurePerformance登录后复制登录后复制登录后复制内部返回的那个匿名函数,这个匿名函数在调用factorial登录后复制登录后复制登录后复制的前后添加了装饰逻辑。

这种方式的优点在于它的独立性和通用性。你可以很地组合多个高层函数装饰器,形成一个功能链。

比如,你可以在measurePerformance登录后复制登录后复制登录后复制再套一个logDecorator登录后复制,先打印日志再测量性能。

当然,也有一些小“缺点”:它不如ES7的@登录后复制登录后复制登录后复制语法,每次都需要手动包装。另外,对于之前类的方法,你需要手动访问原型链上的方法进行包装,不如ES7方法装饰器这样直接作用在方法定义上。但就函数而言,高阶函数装饰器绝对是首选。ES7的装饰器语法有哪些特点与应用场景?

ES7(目前仍然是Stage) 3提议)引入的装饰器语法,用一个@登录后复制登录后复制登录后复制符号,让装饰器的应用变得异常简洁和声明性。它主要针对类和类的成员(方法、属性、Getter/Setter)进行装饰,而不是像高阶函数那样直接装饰普通的函数。这使得在构建大型、结构化的应用程序时,特别是与框架(如Angular、NestJS)结合时,布局特别强大。

特点:声明性语法:直接在类或方法定义上面使用@decoratorName登录后复制,一目了然地表明该类或方法被赋予了额外的功能。作用目标明确:装饰器函数会接收到关于被装饰目标(类、方法等)的特定信息,例如类构造函数、方法名、属性等。这使得装饰器可以精确地或修改增强目标。多个装饰器可以同时安装在同一个目标上,它们会按照从下往上的顺序执行(对于类别和方法装饰器而言,执行顺序是自下向上,但通常会认为最接近目标的装饰器先应用)。 元编程能力: 修改装饰器在运行时提供了定义类或方法行为的能力,这是一种强大的元编程(元编程)工具。

应用场景:

ES7装饰器在实际开发中有着广泛的应用,尤其是在框架和库的开发中,它们能够极大地简化代码并提升竞争力。

日志记录与性能监控:这是最常见的应用。你可以创建一个@log登录后复制装饰器,自动记录方法被调用时的参数和返回值;或者一个@measure登录后复制装饰器,统计方法的执行时间。//假设这是我们的装饰器实现// function log(target, key,descriptor) { /* ... */ }// functionmeasure(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(`删除用户: ${userId}`); }}登录后复制

数据验证:在处理表单输入或API请求时,往往需要对数据进行严格的验证。@validate(schema)登录后复制装饰器可以在方法执行前自动根据预定义的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如何实现装饰器?装饰器模式应用的详细,更多请关注乐哥内容常识网其他相关文章需要!

JS如何实现装饰器?
怎么多个抖音号 多个抖音号如何实名认证
相关内容
发表评论

游客 回复需填写必要信息