JavaScript設計模式----裝飾者模式
阿新 • • 發佈:2019-02-20
宣告:這個系列為閱讀《JavaScript設計模式與開發實踐》 —-曾探@著一書的讀書筆記
裝飾者模式的定義:
裝飾者(decorator)模式能夠在不改變物件自身的基礎上,在程式執行期間給對像動態的新增職責。與繼承相比,裝飾者是一種更輕便靈活的做法。
裝飾者模式的特點:
可以動態的給某個物件新增額外的職責,而不會影響從這個類中派生的其它物件;
繼承的一些缺點:
- 繼承會導致超類和子類之間存在強耦合性,當超類改變時,子類也會隨之改變;
- 超類的內部細節對於子類是可見的,繼承常常被認為破壞了封裝性;
傳統面向物件的裝飾者和JavaScript裝飾者對比:
1.模擬傳統面嚮物件語言的裝飾者模式
//模擬傳統語言的裝飾者
//原始的飛機類
var Plan = function () {
};
Plan.prototype.fire = function () {
console.log('發射普通子彈');
};
//裝飾類
var MissileDecorator = function (plan) {
this.plan = plan;
}
MissileDecorator.prototype.fire = function () {
this.plan.fire();
console.log('發射導彈!');
};
var plan = new Plan();
plan = new MissileDecorator(plan);
plan.fire();
2.JavaScript中的裝飾者模式
裝飾者模式將一個物件嵌入到另一個物件之中,實際上相當於這個物件被另一個對像包裝起來,形成一條包裝鏈。請求隨著這條包裝鏈依次傳遞到所有的物件,每個物件都有處理這條請求的機會。
var Plan1 = {
fire: function () {
console.log('發射普通的子彈');
}
};
var missileDecorator= function () {
console.log('發射導彈!' );
};
var fire = Plan1.fire;
Plan1.fire=function () {
fire();
missileDecorator();
};
Plan1.fire();
裝飾函式
在JavaScript中可以很方便的給某個物件擴充套件屬性和方法,但卻很難在不改動某個函式原始碼的情況下,給該函式新增一些額外的功能。也就是在程式碼執行期間,我們很難切入某個函式的執行環境。
1.使用裝飾者模式例子
//對window.onload的處理
window.onload=function () {
console.log('test');
};
var _onload= window.onload || function () {};
window.onload=function () {
_onload();
console.log('自己的處理函式');
};
2.使用AOP(面向切面程式設計)裝飾函式
主要是以為在JavaScript中會存在隨著函式的呼叫,this
的指向發生變化,導致執行結果發生變化。
2.1.封裝的before函式
在需要執行的函式之前執行某個新新增的功能函式
//是新新增的函式在舊函式之前執行
Function.prototype.before=function (beforefn) {
var _this= this; //儲存舊函式的引用
return function () { //返回包含舊函式和新函式的“代理”函式
beforefn.apply(this,arguments); //執行新函式,且保證this不被劫持,新函式接受的引數
// 也會被原封不動的傳入舊函式,新函式在舊函式之前執行
return _this.apply(this,arguments);
};
};
2.2.封裝的after函式
在需要執行的函式之後執行某個新新增的功能函式
//新新增的函式在舊函式之後執行
Function.prototype.after=function (afterfn) {
var _this=this;
return function () {
var ret=_this.apply(this,arguments);
afterfn.apply(this,arguments);
return ret;
};
};
2.3.不汙染Function原型的做法
var before=function (fn, before) {
return function () {
before.apply(this,arguments);
return fn.apply(this,arguments);
};
};
function func1(){console.log('1')}
function func2() {console.log('2')}
var a=before(func1,func2);
// a=before(a,func1);
a();
裝飾者模式用法示例:
1.ajax動態新增引數
使用裝飾者模式動態的改變ajax函式,傳輸的引數
//是新新增的函式在舊函式之前執行
Function.prototype.before=function (beforefn) {
var _this= this; //儲存舊函式的引用
return function () { //返回包含舊函式和新函式的“代理”函式
beforefn.apply(this,arguments); //執行新函式,且保證this不被劫持,新函式接受的引數
// 也會被原封不動的傳入舊函式,新函式在舊函式之前執行
return _this.apply(this,arguments);
};
};
var func = function (param) {
console.log(param);
};
func = func.before(function (param) {
param.b = 'b';
});
func({b:'222'});
//給ajax請求動態新增引數的例子
var ajax=function (type,url,param) {
console.log(param);
};
var getToken=function () {
return 'Token';
};
ajax=ajax.before(function (type, url, param) {
param.token=getToken();
});
ajax('get','http://www.jn.com',{name:'zhiqiang'});
2.表單驗證並且提交
裝飾者模式分離表單驗證和提交的函式
Function.prototype.before=function (beforefn) {
var _this= this; //儲存舊函式的引用
return function () { //返回包含舊函式和新函式的“代理”函式
beforefn.apply(this,arguments); //執行新函式,且保證this不被劫持,新函式接受的引數
// 也會被原封不動的傳入舊函式,新函式在舊函式之前執行
return _this.apply(this,arguments);
};
};
var validata=function () {
if(username.value===''){
alert('使用者名稱不能為空!')
return false;
}
if(password.value===''){
alert('密碼不能為空!')
return false;
}
}
var formSubmit=function () {
var param={
username=username.value;
password=password.value;
}
ajax('post','http://www.mn.com',param);
}
formSubmit= formSubmit.before(validata);
submitBtn.onclick=function () {
formSubmit();
}
總結:
裝飾者模式和代理模式的區別:
1. 代理模式的目的是,當直接訪問本體不方便或者不符合需要時,為這個本體提供一個代替者。本體定義了關鍵功能,而代理提供了或者拒絕對他的訪問,或者是在訪問本體之前做一些額外的事情。
2. 裝飾者模式的作用就是為物件動態的加入某些行為。