摘要:下裝飾者的實現了解了裝飾者模式和的概念之后,我們寫一段能夠兼容的代碼來實現裝飾者模式原函數拍照片定義函數裝飾函數加濾鏡用裝飾函數裝飾原函數這樣我們就實現了抽離拍照與濾鏡邏輯,如果以后需要自動上傳功能,也可以通過函數來添加。
什么是裝飾者模式
當我們拍了一張照片準備發朋友圈時,許多小伙伴會選擇給照片加上濾鏡。同一張照片、不同的濾鏡組合起來就會有不同的體驗。這里實際上就應用了裝飾者模式:是通過濾鏡裝飾了照片。在不改變對象(照片)的情況下動態的為其添加功能(濾鏡)。
需要注意的是:由于 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 機制很像,其實他們都是“開放-封閉原則”和“單一職責原則”的體現。
附上代碼 jsbin 地址:
ES3
ES5
原型鏈
ES7
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/104942.html
摘要:裝飾者模式定義裝飾者模式能夠在不改變對象自身的基礎上,在程序運行期間給對像動態的添加職責。與繼承相比,裝飾者是一種更輕便靈活的做法。 裝飾者模式 定義 : 裝飾者(decorator)模式能夠在不改變對象自身的基礎上,在程序運行期間給對像動態的添加職責。與繼承相比,裝飾者是一種更輕便靈活的做法。 在不改變對象自身的基礎上,在程序運行期間給對象動態地添加一些額外職責 特點 : 可以動態的...
摘要:用戶名不能為空密碼不能為空校驗未通過使用優化代碼返回的情況直接,不再執行后面的原函數用戶名不能為空密碼不能為空 本文是《JavaScript設計模式與開發實踐》的學習筆記,例子來源于書中,對于設計模式的看法,推薦看看本書作者的建議。 什么是裝飾者模式? 給對象動態增加職責的方式成為裝飾者模式。 裝飾者模式能夠在不改變對象自身的基礎上,在運行程序期間給對象動態地添加職責。這是一種輕便靈活...
摘要:會一直完善下去,歡迎建議和指導,同時也歡迎中用到了那些設計模式中用到了那些設計模式這兩個問題,在面試中比較常見。工廠設計模式使用工廠模式可以通過或創建對象。 我自己總結的Java學習的系統知識點以及面試問題,已經開源,目前已經 41k+ Star。會一直完善下去,歡迎建議和指導,同時也歡迎Star: https://github.com/Snailclimb... JDK 中用到了那...
摘要:但是,這樣做的后果就是,我們會不斷的改變本體,就像把鳳姐送去做整形手術一樣。在中,我們叫做引用裝飾。所以,這里引入的裝飾模式裝飾親切,熟悉,完美。實例講解裝飾上面那個例子,只能算是裝飾模式的一個不起眼的角落。 裝飾者,英文名叫decorator. 所謂的裝飾,從字面可以很容易的理解出,就是給 土肥圓,化個妝,華麗的轉身為白富美,但本體還是土肥圓。 說人話.咳咳~ 在js里面一切都是對...
摘要:設計模式裝飾者模式何為裝飾者,意思就是,在不影響對象主功能的情況下,再添加一些額外的功能,使對象具備更多的功能。與繼承相比,裝飾者是一種更靈活輕便的做法。 javascript設計模式 --- 裝飾者模式 何為裝飾者,意思就是,在不影響對象主功能的情況下,再添加一些額外的功能,使對象具備更多的功能。與繼承相比,裝飾者是一種更靈活輕便的做法。下面我們看看javascript里裝飾者模式 ...
閱讀 2255·2023-04-26 02:14
閱讀 2926·2021-09-30 09:46
閱讀 2101·2021-09-24 09:48
閱讀 952·2021-09-24 09:47
閱讀 3252·2019-08-30 15:44
閱讀 1879·2019-08-30 15:44
閱讀 3279·2019-08-30 14:18
閱讀 1949·2019-08-30 12:58