摘要:裝飾者模式定義裝飾者模式能夠在不改變對(duì)象自身的基礎(chǔ)上,在程序運(yùn)行期間給對(duì)像動(dòng)態(tài)的添加職責(zé)。與繼承相比,裝飾者是一種更輕便靈活的做法。
裝飾者模式 定義 :
裝飾者(decorator)模式能夠在不改變對(duì)象自身的基礎(chǔ)上,在程序運(yùn)行期間給對(duì)像動(dòng)態(tài)的添加職責(zé)。與繼承相比,裝飾者是一種更輕便靈活的做法。
在不改變對(duì)象自身的基礎(chǔ)上,在程序運(yùn)行期間給對(duì)象動(dòng)態(tài)地添加一些額外職責(zé)
特點(diǎn) :可以動(dòng)態(tài)的給某個(gè)對(duì)象添加額外的職責(zé),而不會(huì)影響從這個(gè)類中派生的其它對(duì)象;
作用:裝飾者模式的作用就是為對(duì)象動(dòng)態(tài)的加入某些行為。
為什么要使用裝飾者模式在傳統(tǒng)面向?qū)ο笳Z(yǔ)言中,為對(duì)象添加功能常使用繼承
但是繼承有很多缺點(diǎn):
超類子類強(qiáng)耦合,超類改變導(dǎo)致子類改變
超類內(nèi)部細(xì)節(jié)對(duì)子類可見(jiàn),破壞了封裝性
完成功能復(fù)用同時(shí),可能會(huì)創(chuàng)造大量子類
舉例:
var person = { name: "payen", sex: "male" } person.age = "20";
裝飾者模式是在不改變對(duì)象自身的基礎(chǔ)上
而我們改變了原對(duì)象 ,所以上面這個(gè)例子不是裝飾器
/*模擬傳統(tǒng)語(yǔ)言的裝飾者 裝飾者模式將一個(gè)對(duì)象嵌入到另一個(gè)對(duì)象之中, 實(shí)際上相當(dāng)于這個(gè)對(duì)象被另一個(gè)對(duì)像包裝起來(lái),形成一條包裝鏈。 請(qǐng)求隨著這條包裝鏈依次傳遞到所有的對(duì)象,每個(gè)對(duì)象都有處理這條請(qǐng)求的機(jī)會(huì)。 */ //原始的飛機(jī)類 var Plan = function () { }; Plan.prototype.fire = function () { console.log("發(fā)射普通子彈"); }; //裝飾類 var MissileDecorator = function (plan) { this.plan = plan; }; MissileDecorator.prototype.fire = function () { this.plan.fire(); console.log("發(fā)射導(dǎo)彈!"); }; var plan = new Plan(); plan = new MissileDecorator(plan); plan.fire();
這樣給對(duì)象動(dòng)態(tài)的增加職責(zé)的方式就沒(méi)有改變對(duì)象自身
一個(gè)對(duì)象放入另一個(gè)對(duì)象
形成了一條裝飾鏈(一個(gè)聚合對(duì)象)
而上面的shot和track也就是是裝飾者、裝飾函數(shù)
當(dāng)函數(shù)執(zhí)行時(shí),會(huì)把請(qǐng)求轉(zhuǎn)給鏈中的下一個(gè)對(duì)象
在JavaScript中,很容易給對(duì)象擴(kuò)展屬性與方法
但是卻不容易給函數(shù)擴(kuò)展額外功能,除非改函數(shù)源碼
但是改寫函數(shù)違反了開(kāi)放-封閉原則
var foo = function(){ console.log(1); } //改為 var foo = function(){ console.log(1); console.log(2);//增 }
一個(gè)常用的方法就是緩存函數(shù)引用,改寫函數(shù)
var foo = function(){ console.log(1); } //改為 var foo = function(){ console.log(1); } var _foo = foo; foo = function(){ _foo(); console.log(2); }
但是這樣寫還是存在問(wèn)題
*要維護(hù)額外的中間變量(_foo),如果裝飾鏈過(guò)長(zhǎng),中間變量就會(huì)越來(lái)越多
可能會(huì)存在this被劫持問(wèn)題*
關(guān)于this劫持問(wèn)題,看下面的例子:
var getId = document.getElementById; document.getElementById = function(ID){ console.log(1); return getId(ID); } document.getElementById("demo");
因?yàn)槭褂?document.getElementById 的時(shí)候
內(nèi)部有this引用,而這個(gè)this期望指向的是document
但是 getId 在獲取了 document.getElementById 引用后
this就指向了window,導(dǎo)致拋出錯(cuò)誤
為了讓this正確指向document
我們可以做出修改:
var getId = document.getElementById; document.getElementById = function(ID){ console.log(1); return getId.call(document, ID); } document.getElementById("demo");AOP裝飾函數(shù)
AOP(Aspect Oriented Programming)面向切面編程
把一些與核心業(yè)務(wù)邏輯無(wú)關(guān)的功能抽離出來(lái)
再通過(guò)“動(dòng)態(tài)織入”方式摻入業(yè)務(wù)邏輯模塊
與業(yè)務(wù)邏輯無(wú)關(guān)的功能通常包括日志統(tǒng)計(jì)、安全控制、異常處理等等
好處也很明顯,保證了核心業(yè)務(wù)模塊的純凈與高內(nèi)聚性
而且其他的功能模塊也可以很好的復(fù)用
首先我們要實(shí)現(xiàn)兩個(gè)函數(shù)
一個(gè)用來(lái)前置裝飾,一個(gè)用來(lái)后置裝飾:
Function.prototype.before = function(beforeFunc){ var that = this; return function(){ beforeFunc.apply(this, arguments); return that.apply(this, arguments); } } Function.prototype.after = function(afterFunc){ var that = this; return function(){ var ret = that.apply(this, arguments); afterFunc.apply(this, arguments); return ret; } }
以前置裝飾為例
調(diào)用before時(shí),先把原函數(shù)的引用保存下來(lái)
然后返回一個(gè)“代理”函數(shù)
這樣在原函數(shù)調(diào)用前,先執(zhí)行擴(kuò)展功能的函數(shù)
而且他們共用同一個(gè)參數(shù)列表
后置裝飾與前置裝飾基本類似,只是執(zhí)行順序不同
如果不喜歡這種污染原型的方式,也可以這么寫:
var before = function(originFunc, beforeFunc){ return function(){ before.apply(this, arguments); return originFunc.apply(this, arguments); } } var after = function(originFunc, afterFunc){ return function(){ var ret = originFunc.apply(this, arguments); afterFunc.apply(this, arguments); return ret; } }
使用這種AOP的方式可以完美的對(duì)函數(shù)進(jìn)行功能擴(kuò)展
var foobar = function(x, y, z){ console.log(x, y, z); } var foo = function(x, y, z){ console.log(x/10, y/10, z/10); } var bar = function(x, y, z){ console.log(x*10, y*10, z*10); } foobar = foobar.before(foo).after(bar); foobar(1, 2, 3); //0.1 0.2 0.3 //1 2 3 //10 20 30
由于原函數(shù)和裝飾函數(shù)共用一個(gè)參數(shù)列表
所以我們可以用AOP改變函數(shù)參數(shù)
var data = { width: "100px", height: "100px" } var demo = function(data){ console.log(JSON.stringify(data)); } demo = demo.before(function(data){ data.color = "red"; }); demo(data); //{"width":"100px","height":"100px","color":"red"}
最后談一談裝飾者模式的缺點(diǎn)
它也不是十全十美的
裝飾鏈疊加了函數(shù)作用域,如果過(guò)長(zhǎng)也會(huì)產(chǎn)生性能問(wèn)題
如果原函數(shù)上保存了屬性,返回新函數(shù)后屬性會(huì)丟失
var demo = function(){ console.log(1); } demo.a = 123; demo = demo.after(function(){ console.log(2); }); demo(); console.log(demo.a); //undefined
裝飾者模式在開(kāi)發(fā)中非常有用,在框架開(kāi)發(fā)中也十分有用
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/88798.html
摘要:聲明這個(gè)系列為閱讀設(shè)計(jì)模式與開(kāi)發(fā)實(shí)踐曾探著一書的讀書筆記裝飾者模式的定義裝飾者模式能夠在不改變對(duì)象自身的基礎(chǔ)上,在程序運(yùn)行期間給對(duì)像動(dòng)態(tài)的添加職責(zé)。與繼承相比,裝飾者是一種更輕便靈活的做法。裝飾者模式的作用就是為對(duì)象動(dòng)態(tài)的加入某些行為。 聲明:這個(gè)系列為閱讀《JavaScript設(shè)計(jì)模式與開(kāi)發(fā)實(shí)踐》 ----曾探@著一書的讀書筆記 裝飾者模式的定義: 裝飾者(decorator)模式能...
摘要:下裝飾者的實(shí)現(xiàn)了解了裝飾者模式和的概念之后,我們寫一段能夠兼容的代碼來(lái)實(shí)現(xiàn)裝飾者模式原函數(shù)拍照片定義函數(shù)裝飾函數(shù)加濾鏡用裝飾函數(shù)裝飾原函數(shù)這樣我們就實(shí)現(xiàn)了抽離拍照與濾鏡邏輯,如果以后需要自動(dòng)上傳功能,也可以通過(guò)函數(shù)來(lái)添加。 showImg(https://segmentfault.com/img/bVbueyz?w=852&h=356); 什么是裝飾者模式 當(dāng)我們拍了一張照片準(zhǔn)備發(fā)朋友...
摘要:設(shè)計(jì)模式裝飾者模式何為裝飾者,意思就是,在不影響對(duì)象主功能的情況下,再添加一些額外的功能,使對(duì)象具備更多的功能。與繼承相比,裝飾者是一種更靈活輕便的做法。 javascript設(shè)計(jì)模式 --- 裝飾者模式 何為裝飾者,意思就是,在不影響對(duì)象主功能的情況下,再添加一些額外的功能,使對(duì)象具備更多的功能。與繼承相比,裝飾者是一種更靈活輕便的做法。下面我們看看javascript里裝飾者模式 ...
摘要:裝飾者模式裝飾者模式提供比繼承更有彈性的替代方案。裝飾者用于包裝同接口的對(duì)象,用于通過(guò)重載方法的形式添加新功能,該模式可以在被裝飾者的前面或后面加上自己的行為以達(dá)到特定的目的。簡(jiǎn)單的理解給對(duì)象動(dòng)態(tài)添加職責(zé)的方式稱為裝飾著模式。 裝飾者模式 裝飾者模式提供比繼承更有彈性的替代方案。裝飾者用于包裝同接口的對(duì)象,用于通過(guò)重載方法的形式添加新功能,該模式可以在被裝飾者的前面或后面加上自己的行為...
摘要:?jiǎn)误w模式有以下優(yōu)點(diǎn)用來(lái)劃分命名空間,減少全局變量數(shù)量。通常我們使用操作符創(chuàng)建單體模式的三種選擇,讓構(gòu)造函數(shù)總返回最初的對(duì)象使用全局對(duì)象來(lái)存儲(chǔ)該實(shí)例不推薦,容易全局污染。實(shí)現(xiàn)該工廠模式并不困難,主要是要找到能夠穿件所需類型對(duì)象的構(gòu)造函數(shù)。 介紹 最近開(kāi)始給自己每周訂個(gè)學(xué)習(xí)任務(wù),學(xué)習(xí)結(jié)果反饋為一篇文章的輸出,做好學(xué)習(xí)記錄。 這一周(02.25-03.03)我定的目標(biāo)是《JavaScri...
閱讀 2335·2021-11-25 09:43
閱讀 2960·2019-08-30 15:52
閱讀 1919·2019-08-30 15:44
閱讀 999·2019-08-30 10:58
閱讀 784·2019-08-29 18:43
閱讀 3241·2019-08-29 18:36
閱讀 2339·2019-08-29 17:02
閱讀 1479·2019-08-29 17:01