您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關JavaScript中裝飾者模式與AOP的示例分析,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
什么是裝飾者模式
當我們拍了一張照片準備發(fā)朋友圈時,許多小伙伴會選擇給照片加上濾鏡。同一張照片、不同的濾鏡組合起來就會有不同的體驗。這里實際上就應用了裝飾者模式:是通過濾鏡裝飾了照片。在不改變對象(照片)的情況下動態(tài)的為其添加功能(濾鏡)。
需要注意的是:由于 JavaScript 語言動態(tài)的特性,我們很容易就能改變某個對象(JavaScript 中函數(shù)是一等公民)。但是我們要盡量避免直接改寫某個函數(shù),這會導致代碼的可維護性、可擴展性變差,甚至會污染其他業(yè)務。
什么是 AOP
想必大家對"餐前洗手、飯后漱口"都不陌生。這句標語其實就是 AOP 在生活中的例子:吃飯這個動作相當于切點,我們可以在這個切點前、后插入其它如洗手等動作。
AOP(Aspect-Oriented Programming):面向切面編程,是對 OOP 的補充。利用AOP可以對業(yè)務邏輯的各個部分進行隔離,也可以隔離業(yè)務無關的功能比如日志上報、異常處理等,從而使得業(yè)務邏輯各部分之間的耦合度降低,提高業(yè)務無關的功能的復用性,也就提高了開發(fā)的效率。
在 JavaScript 中,我們可以通過裝飾者模式來實現(xiàn) AOP,但是兩者并不是一個維度的概念。 AOP 是一種編程范式,而裝飾者是一種設計模式。
ES3 下裝飾者的實現(xiàn)
了解了裝飾者模式和 AOP 的概念之后,我們寫一段能夠兼容 ES3 的代碼來實現(xiàn)裝飾者模式:
// 原函數(shù) var takePhoto =function(){ console.log('拍照片'); } // 定義 aop 函數(shù) var after=function( fn, afterfn ){ return function(){ let res = fn.apply( this, arguments ); afterfn.apply( this, arguments ); return res; } } // 裝飾函數(shù) var addFilter=function(){ console.log('加濾鏡'); } // 用裝飾函數(shù)裝飾原函數(shù) takePhoto=after(takePhoto,addFilter); takePhoto();
這樣我們就實現(xiàn)了抽離拍照與濾鏡邏輯,如果以后需要自動上傳功能,也可以通過aop函數(shù)after來添加。
ES5 下裝飾者的實現(xiàn)
在 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()
基于原型鏈和類的裝飾者實現(xiàn)
我們知道,在 JavaScript 中,函數(shù)也好,類也好都有著自己的原型,通過原型鏈我們也能夠很方便的動態(tài)擴展,以下是基于原型鏈的寫法:
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 函數(shù)修飾原函數(shù) after(Test, 'takePhoto', () => { console.log('添加濾鏡'); }); let t = new Test(); t.takePhoto();
使用 ES7 修飾器實現(xiàn)裝飾者
在 ES7 中引入了@decorator 修飾器的提案,參考阮一峰的文章。修飾器是一個函數(shù),用來修改類的行為。目前Babel轉碼器已經支持。注意修飾器只能裝飾類或者類屬性、方法。三者的具體區(qū)別請參考 MDN Object.defineProperty ;而 TypeScript 的實現(xiàn)又有所不同:TypeScript Decorator。
接下來我們通過修飾器來實現(xiàn)對方法的裝飾:
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()
可以看到,使用修飾器的代碼非常簡潔明了。
場景:性能上報
裝飾者模式可以應用在很多場景,典型的場景是記錄某異步請求請求耗時的性能數(shù)據(jù)并上報:
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函數(shù)不會影響業(yè)務代碼,反之亦然。
場景:異常處理
我們可以對原有代碼進行簡單的異常處理,而無需侵入式的修改:
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()
通過以上兩個示例我們可以看到,修飾器的定義很簡單,功能卻非常強大。
關于“JavaScript中裝飾者模式與AOP的示例分析”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經查實,將立刻刪除涉嫌侵權內容。