什么是装饰者模式
当我们拍了一张照片准备发朋友圈时,许多小伙伴会选择给照片加上滤镜。同一张照片、不同的滤镜组合起来就会有不同的体验。这里实际上就应用了装饰者模式:是通过滤镜装饰了照片。在不改变对象(照片)的情况下动态的为其添加功能(滤镜)。
需要注意的是:由于 JavaScript 语言动态的特性,我们很容易就能改变某个对象(JavaScript 中函数是一等公民)。但是我们要尽量避免直接改写某个函数,这会导致代码的可维护性、可扩展性变差,甚至会污染其他业务。
什么是 AOP
想必大家对"餐前洗手、饭后漱口"都不陌生。这句标语其实就是 AOP 在生活中的例子:吃饭这个动作相当于切点,我们可以在这个切点前、后插入其它如洗手等动作。
AOP(Aspect-Oriented Programming):面向切面编程,是对 OOP 的补充。利用AOP可以对业务逻辑的各个部分进行隔离,也可以隔离业务无关的功能比如日志上报、异常处理等,从而使得业务逻辑各部分之间的耦合度降低,提高业务无关的功能的复用性,也就提高了开发的效率。
在 JavaScript 中,我们可以通过装饰者模式来实现 AOP,但是两者并不是一个维度的概念。 AOP 是一种编程范式,而装饰者是一种设计模式。
ES3 下装饰者的实现
了解了装饰者模式和 AOP 的概念之后,我们写一段能够兼容 ES3 的代码来实现装饰者模式:
// 原函数 var takePhoto =function(){ console.log('拍照片'); } // 定义 aop 函数 var after=function( fn, afterfn ){ return function(){ let res = fn.apply( this, arguments ); afterfn.apply( this, arguments ); return res; } } // 装饰函数 var addFilter=function(){ console.log('加滤镜'); } // 用装饰函数装饰原函数 takePhoto=after(takePhoto,addFilter); takePhoto();
这样我们就实现了抽离拍照与滤镜逻辑,如果以后需要自动上传功能,也可以通过aop函数after来添加。
ES5 下装饰者的实现
在 ES5 中引入了Object.defineProperty,我们可以更方便的给对象添加属性:
let takePhoto = function () { console.log('拍照片'); } // 给 takePhoto 添加属性 after Object.defineProperty(takePhoto, 'after', { writable: true, value: function () { console.log('加滤镜'); }, }); // 给 takePhoto 添加属性 before Object.defineProperty(takePhoto, 'before', { writable: true, value: function () { console.log('打开相机'); }, }); // 包装方法 let aop = function (fn) { return function () { fn.before() fn() fn.after() } } takePhoto = aop(takePhoto) takePhoto()
基于原型链和类的装饰者实现
我们知道,在 JavaScript 中,函数也好,类也好都有着自己的原型,通过原型链我们也能够很方便的动态扩展,以下是基于原型链的写法:
class Test { takePhoto() { console.log('拍照'); } } // after AOP function after(target, action, fn) { let old = target.prototype[action]; if (old) { target.prototype[action] = function () { let self = this; fn.bind(self); fn(handle); } } } // 用 AOP 函数修饰原函数 after(Test, 'takePhoto', () => { console.log('添加滤镜'); }); let t = new Test(); t.takePhoto();
使用 ES7 修饰器实现装饰者
在 ES7 中引入了@decorator 修饰器的提案,参考阮一峰的文章。修饰器是一个函数,用来修改类的行为。目前Babel转码器已经支持。注意修饰器只能装饰类或者类属性、方法。三者的具体区别请参考 MDN Object.defineProperty ;而 TypeScript 的实现又有所不同:TypeScript Decorator。
接下来我们通过修饰器来实现对方法的装饰:
function after(target, key, desc) { const { value } = desc; desc.value = function (...args) { let res = value.apply(this, args); console.log('加滤镜') return res; } return desc; } class Test{ @after takePhoto(){ console.log('拍照') } } let t = new Test() t.takePhoto()
可以看到,使用修饰器的代码非常简洁明了。
场景:性能上报
装饰者模式可以应用在很多场景,典型的场景是记录某异步请求请求耗时的性能数据并上报:
function report(target, key, desc) { const { value } = desc; desc.value = async function (...args) { let start = Date.now(); let res = await value.apply(this, args); let millis = Date.now()-start; // 上报代码 return res; } return desc; } class Test{ @report getData(url){ // fetch 代码 } } let t = new Test() t.getData()
这样使用@report修饰后的代码就会上报请求所消耗的时间。扩展或者修改report函数不会影响业务代码,反之亦然。
场景:异常处理
我们可以对原有代码进行简单的异常处理,而无需侵入式的修改:
function handleError(target, key, desc) { const { value } = desc; desc.value = async function (...args) { let res; try{ res = await value.apply(this, args); }catch(err){ // 异常处理 logger.error(err) } return res; } return desc; } class Test{ @handleError getData(url){ // fetch 代码 } } let t = new Test() t.getData()
通过以上两个示例我们可以看到,修饰器的定义很简单,功能却非常强大。
小结
我们一步一步通过高阶函数、原型链、Object.defineProperty和@Decorator分别实现了装饰者模式。接下来在回顾一下:
- 装饰者模式非常适合给业务代码附加非业务相关功能(如日志上报),就如同给照片加滤镜;
- 装饰者模式非常适合无痛扩展别人的代码(你经常需要接手别人的项目吧)
有些朋友可能会觉得装饰者模式和 vue 的 mixin 机制很像,其实他们都是“开放-封闭原则”和“单一职责原则”的体现。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 中国武警男声合唱团《辉煌之声1天路》[DTS-WAV分轨]
- 紫薇《旧曲新韵》[320K/MP3][175.29MB]
- 紫薇《旧曲新韵》[FLAC/分轨][550.18MB]
- 周深《反深代词》[先听版][320K/MP3][72.71MB]
- 李佳薇.2024-会发光的【黑籁音乐】【FLAC分轨】
- 后弦.2012-很有爱【天浩盛世】【WAV+CUE】
- 林俊吉.2012-将你惜命命【美华】【WAV+CUE】
- 晓雅《分享》DTS-WAV
- 黑鸭子2008-飞歌[首版][WAV+CUE]
- 黄乙玲1989-水泼落地难收回[日本天龙版][WAV+CUE]
- 周深《反深代词》[先听版][FLAC/分轨][310.97MB]
- 姜育恒1984《什么时候·串起又散落》台湾复刻版[WAV+CUE][1G]
- 那英《如今》引进版[WAV+CUE][1G]
- 蔡幸娟.1991-真的让我爱你吗【飞碟】【WAV+CUE】
- 群星.2024-好团圆电视剧原声带【TME】【FLAC分轨】