12 裝飾者模式
裝飾者模式定義為給對(duì)象動(dòng)態(tài)地增加職責(zé)的方式;裝飾者模式能夠在不改變對(duì)象自身的基礎(chǔ)上疼邀,在程序運(yùn)行期間給對(duì)象動(dòng)態(tài)地添加職責(zé);
12.1 裝飾函數(shù)
在 JavaScript 中可以很方便地給某個(gè)對(duì)象擴(kuò)展屬性和方法,但卻很難在不改動(dòng)某個(gè)函數(shù)源代碼的情況下拐袜,給該函數(shù)添加一些額外的功能通過(guò)保存原引用的方式就可以改寫某個(gè)函數(shù)吉嚣,如下:
window.onload = function(){ alert (1); }
var _onload = window.onload || function(){};
window.onload = function(){
_onload();
alert (2);
}
這樣的代碼符合開放-封閉原則的,在增加新功能的時(shí)候蹬铺,確實(shí)沒(méi)有修改原來(lái)的 window.onload
代碼尝哆,但是這種方式存在以下兩個(gè)問(wèn)題:
- 必須維護(hù)
_onload
這個(gè)中間變量,如果函數(shù)的裝飾鏈較長(zhǎng)丛塌,或者需要裝飾的函數(shù)變多较解,這些中間變量的數(shù)量也會(huì)越來(lái)越多畜疾; - 其實(shí)還遇到了
this
被劫持的問(wèn)題赴邻,遇到該問(wèn)題時(shí)需要 用Function.prototype.apply()
手動(dòng)設(shè)置this
指向,如下document.getElementById
方法啡捶;
<html>
<button id="button"></button>
<script>
var _getElementById = document.getElementById;
document.getElementById = function(){
alert (1);
return _getElementById.apply( document, arguments );
}
var button = document.getElementById( 'button' );
</script>
</html>
12.2 用 AOP (面向切面編程)裝飾函數(shù)
Function.prototype.before
方法和Function.prototype.after
方法實(shí)現(xiàn):
Function.prototype.before = function( beforefn ){
var __self = this; // 保存原函數(shù)的引用
return function(){ // 返回包含了原函數(shù)和新函數(shù)的"代理"函數(shù)
beforefn.apply( this, arguments ); // 執(zhí)行新函數(shù)姥敛,且保證 this 不被劫持,新函數(shù)接受的參數(shù)
// 也會(huì)被原封不動(dòng)地傳入原函數(shù)瞎暑,新函數(shù)在原函數(shù)之前執(zhí)行
return __self.apply( this, arguments ); // 執(zhí)行原函數(shù)并返回原函數(shù)的執(zhí)行結(jié)果彤敛,
// 并且保證 this 不被劫持
}
}
Function.prototype.after = function( afterfn ){
var __self = this;
return function(){
var ret = __self.apply( this, arguments );
afterfn.apply( this, arguments );
return ret;
}
};
2.用 AOP 裝飾函數(shù)修改上面例子:
<html>
<button id="button"></button>
<script>
Function.prototype.before = function( beforefn ){
var __self = this;
return function(){
beforefn.apply( this, arguments );
return __self.apply( this, arguments );
}
}
document.getElementById = document.getElementById.before(function(){ alert (1); });
var button = document.getElementById( 'button' );
console.log( button );
</script>
</html>
12.3 AOP 的應(yīng)用實(shí)例
12.4 裝飾者模式和代理模式
代理模式和裝飾者模式最重要的區(qū)別在于它們的意圖和設(shè)計(jì)目的。
- 代理模式:當(dāng)直接訪問(wèn)本體不方便或者不符合需要時(shí)了赌,為這個(gè)本體提供一個(gè)替代者墨榄,本體定義了關(guān)鍵功能,而代理提供或拒絕對(duì)它的訪問(wèn)勿她,或者在訪問(wèn)本體之前做一些額外的事情袄秩;代理模式強(qiáng)調(diào)一種關(guān)系( Proxy 與它的實(shí)體之間的關(guān)系),這種關(guān)系可以靜態(tài)的表達(dá)逢并,在一開始就可以被確定之剧;
- 裝飾者模式:為對(duì)象動(dòng)態(tài)加入行為;裝飾者模式用于一開始不能確定對(duì)象的全部功能時(shí)砍聊;代理模式通常只有一層代理-本體的引用背稼,而裝飾者模式經(jīng)常會(huì)形成一條長(zhǎng)長(zhǎng)的裝飾鏈;
12.5 裝飾者模式小結(jié)
裝飾函數(shù)是 JavaScript 中獨(dú)特的裝飾者模式玻蝌,這種模式在實(shí)際開發(fā)中非常有用蟹肘;同時(shí)在框架開發(fā)中也十分有用词疼,通過(guò)裝飾者模式為框架里的函數(shù)提供的一些穩(wěn)定而方便移植的功能,這些個(gè)性化的功能可以在框架之外動(dòng)態(tài)裝飾上去帘腹,這樣能避免為了讓框架擁有更多的功能寒跳,而去使用一些 if、 else 語(yǔ)句預(yù)測(cè)用戶的實(shí)際需要竹椒;
系列鏈接
- JavaScript 設(shè)計(jì)模式(上)——基礎(chǔ)知識(shí)
- JavaScript 設(shè)計(jì)模式(中)——1.單例模式
- JavaScript 設(shè)計(jì)模式(中)——2.策略模式
- JavaScript 設(shè)計(jì)模式(中)——3.代理模式
- JavaScript 設(shè)計(jì)模式(中)——4.迭代器模式
- JavaScript 設(shè)計(jì)模式(中)——5.發(fā)布訂閱模式
- JavaScript 設(shè)計(jì)模式(中)——6.命令模式
- JavaScript 設(shè)計(jì)模式(中)——7.組合模式
- JavaScript 設(shè)計(jì)模式(中)——8.模板方法模式
- JavaScript 設(shè)計(jì)模式(中)——9.享元模式
- JavaScript 設(shè)計(jì)模式(中)——10.職責(zé)鏈模式
- JavaScript 設(shè)計(jì)模式(中)——11. 中介者模式
- JavaScript 設(shè)計(jì)模式(中)——12. 裝飾者模式
- JavaScript 設(shè)計(jì)模式(中)——13.狀態(tài)模式
- JavaScript 設(shè)計(jì)模式(中)——14.適配器模式
- JavaScript 設(shè)計(jì)模式(下)——設(shè)計(jì)原則
- JavaScript 設(shè)計(jì)模式練習(xí)代碼