您好,登錄后才能下訂單哦!
這篇文章主要為大家展示了“javascript設(shè)計(jì)模式之裝飾者模式怎么用”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“javascript設(shè)計(jì)模式之裝飾者模式怎么用”這篇文章吧。
在js函數(shù)開發(fā)中,想要為現(xiàn)有函數(shù)添加與現(xiàn)有功能無關(guān)的新功能時(shí),按普通思路肯定是在現(xiàn)有函數(shù)中添加新功能的代碼。這并不能說錯(cuò),但因?yàn)楹瘮?shù)中的這兩塊代碼其實(shí)并無關(guān)聯(lián),后期維護(hù)成本會明顯增大,也會造成函數(shù)臃腫。
比較好的辦法就是采用裝飾器模式。在保持現(xiàn)有函數(shù)及其內(nèi)部代碼實(shí)現(xiàn)不變的前提下,將新功能函數(shù)分離開來,然后將其通過與現(xiàn)有函數(shù)包裝起來一起執(zhí)行。
先來看個(gè)比較原始的js版裝飾器模式實(shí)現(xiàn):
var Plane = function(){} Plane.prototype.fire = function(){ console.log('發(fā)射普通子彈'); } //增加兩個(gè)裝飾類,導(dǎo)彈類和原子彈類 var MissileDecorator = function(plane){ this.plane = plane; } MissileDecorator.prototype.fire = function(){ this.plane.fire(); console.log('發(fā)射導(dǎo)彈'); } var AtomDecorator = function(plane){ this.plane = plane; } AtomDecorator.prototype.fire = function(){ this.plane.fire(); console.log('發(fā)射原子彈'); } var plane = new Plane(); console.log(plane); plane = new MissileDecorator(plane); console.log(plane); plane = new AtomDecorator(plane); console.log(plane); plane.fire(); /* 發(fā)射普通子彈 發(fā)射導(dǎo)彈 發(fā)射原子彈 */
升級版裝飾器模式,通過為js的Function構(gòu)造函數(shù)添加實(shí)例方法before和after來實(shí)現(xiàn)。
Function.prototype.before和Function.prototype.after接收一個(gè)函數(shù)作為參數(shù),這個(gè)函數(shù)就是新添加的函數(shù),它裝載了新添加的功能代碼。
接下來把當(dāng)前的this保存起來,這個(gè)this指向原函數(shù)(Function是js中所有函數(shù)的構(gòu)造器,所以js中的函數(shù)都是Function的實(shí)例,F(xiàn)unction.prototype中的this就指向該實(shí)例函數(shù))
然后返回一個(gè)'代理'函數(shù),這個(gè)代理函數(shù)只是結(jié)構(gòu)上像'代理'而已,并不承擔(dān)代理的職責(zé)(比如控制對象的訪問)。它的工作就是把請求分別轉(zhuǎn)發(fā)給新添加的函數(shù)和原函數(shù),且負(fù)責(zé)保證它們的執(zhí)行順序,讓新添加的函數(shù)在原函數(shù)之前執(zhí)行(前置裝飾 Function.prototype.before; 后置裝飾 Function.prototype.after),從而實(shí)現(xiàn)動態(tài)裝飾的效果。
// AOP 裝飾函數(shù) Function.prototype.before = function(beforefn){ var _self = this; //保存原函數(shù)的引用 return function(){ //返回包含了原函數(shù)和新函數(shù)的‘代理'函數(shù) beforefn.apply(this, arguments); //先執(zhí)行新函數(shù),且保證this不會被劫持,新函數(shù)接受的參數(shù)也會原封不動的傳入原函數(shù),新函數(shù)在原函數(shù)之前執(zhí)行 return _self.apply(this, arguments); //再執(zhí)行原函數(shù)并返回原函數(shù)的執(zhí)行結(jié)果,并保證this不被劫持 } } Function.prototype.after = function(afterfn){ var _self = this; //保存原函數(shù)的引用 return function(){ //返回包含了原函數(shù)和新函數(shù)的‘代理'函數(shù) var ret = _self.apply(this, arguments); //先執(zhí)行原函數(shù)并返回原函數(shù)的執(zhí)行結(jié)果,并保證this不被劫持,原函數(shù)執(zhí)行的結(jié)果會賦值給ret變量,交由'代理'函數(shù)最后return afterfn.apply(this, arguments); //再執(zhí)行新函數(shù),且保證this不會被劫持,新函數(shù)接受的參數(shù)也會原封不動的傳入原函數(shù),新函數(shù)在原函數(shù)之前執(zhí)行 return ret; } } //定義原函數(shù) var print = function(){ console.log('打印原函數(shù)執(zhí)行結(jié)果'); } print = print.before(function(){ console.log('打印前置裝飾函數(shù)的執(zhí)行結(jié)果'); }) print = print.after(function(){ console.log('打印后置裝飾函數(shù)的執(zhí)行結(jié)果'); }) //執(zhí)行裝飾后的print函數(shù),為原函數(shù)print函數(shù)添加的裝飾器對用戶來說看起來是透明的 print(); //打印結(jié)果 /* 打印前置裝飾函數(shù)的執(zhí)行結(jié)果 打印原函數(shù)執(zhí)行結(jié)果 打印后置裝飾函數(shù)的執(zhí)行結(jié)果 */
上例中的AOP裝飾器是通過在Function.prototype上添加before和after方法實(shí)現(xiàn)的,但有時(shí)這種直接污染函數(shù)原型的方法并不好,可以做些變通,把原函數(shù)和新函數(shù)都作為參數(shù)傳入before和after方法中
var before = function(fn, beforefn){ return function(){ beforefn.apply(this, arguments) return fn.apply(this, arguments) } } var after = function(fn, agterfn){ return function(){ var ret = fn.apply(this, arguments) agterfn.apply(this, arguments) return ret; } } var a = function(){ console.log('原函數(shù)執(zhí)行結(jié)果'); } a = before(a, function(){ console.log('前置裝飾函數(shù)執(zhí)行結(jié)果'); }) a = after(a, function(){ console.log('后置裝飾函數(shù)執(zhí)行結(jié)果'); }) a() /* 前置裝飾函數(shù)執(zhí)行結(jié)果 原函數(shù)執(zhí)行結(jié)果 后置裝飾函數(shù)執(zhí)行結(jié)果 */
最后再來個(gè)裝飾器模式的實(shí)例應(yīng)用。
在實(shí)際開發(fā)中比較常見的需求是用戶數(shù)據(jù)上報(bào),一般會在項(xiàng)目開發(fā)差不多后,陸續(xù)有此類需求提出,但此時(shí)如果要在對應(yīng)函數(shù)中添加數(shù)據(jù)上報(bào)功能代碼時(shí),就會改動原有函數(shù),既麻煩又增加開發(fā)測試成本。此時(shí)最好的就是使用裝飾器模式通過將上報(bào)函數(shù)裝飾到原有函數(shù)上。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>裝飾者模式應(yīng)用數(shù)據(jù)上報(bào)</title> </head> <body> <button type="button" id="btn">點(diǎn)擊登錄并上報(bào)數(shù)據(jù)</button> </body> <script> var showDialog = function(){ console.log('顯示登錄彈窗'); } var log = function(){ console.log('計(jì)數(shù)上報(bào)'); } var after = function(fn, afterFn){ return function(){ var ret = fn.apply(this, arguments) afterFn.apply(this, arguments) return ret; } } showDialog = after(showDialog, log) document.getElementById('btn').onclick = showDialog; </script> </html>
以上是“javascript設(shè)計(jì)模式之裝飾者模式怎么用”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。