裝飾者(decorator)模式可以在不改變對象自身的基礎上,在程序運行期間給對像動態的添加職責。與繼承相比,裝飾者是一種更輕便靈活的作法。javascript
能夠動態的給某個對象添加額外的職責,而不會影響從這個類中派生的其它對象;java
繼承會致使超類和子類之間存在強耦合性,當超類改變時,子類也會隨之改變;git
超類的內部細節對於子類是可見的,繼承經常被認爲破壞了封裝性;github
//模擬傳統語言的裝飾者 //原始的飛機類 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();
裝飾者模式將一個對象嵌入到另外一個對象之中,實際上至關於這個對象被另外一個對像包裝起來,造成一條包裝鏈。請求隨着這條包裝鏈依次傳遞到全部的對象,每一個對象都有處理這條請求的機會。ajax
var Plan1 = { fire: function () { console.log('發射普通的子彈'); } }; var missileDecorator= function () { console.log('發射導彈!'); }; var fire = Plan1.fire; Plan1.fire=function () { fire(); missileDecorator(); }; Plan1.fire();
在JavaScript中能夠很方便的給某個對象擴展屬性和方法,但卻很難在不改動某個函數源代碼的狀況下,給該函數添加一些額外的功能。也就是在代碼運行期間,咱們很難切入某個函數的執行環境。編程
//對window.onload的處理 window.onload=function () { console.log('test'); }; var _onload= window.onload || function () {}; window.onload=function () { _onload(); console.log('本身的處理函數'); };
主要是覺得在JavaScript中會存在隨着函數的調用,this
的指向發生變化,致使執行結果發生變化。markdown
在須要執行的函數以前執行某個新添加的功能函數app
//是新添加的函數在舊函數以前執行 Function.prototype.before=function (beforefn) { var _this= this; //保存舊函數的引用 return function () { //返回包含舊函數和新函數的「代理」函數 beforefn.apply(this,arguments); //執行新函數,且保證this不被劫持,新函數接受的參數 // 也會被原封不動的傳入舊函數,新函數在舊函數以前執行 return _this.apply(this,arguments); }; };
在須要執行的函數以後執行某個新添加的功能函數函數
//新添加的函數在舊函數以後執行 Function.prototype.after=function (afterfn) { var _this=this; return function () { var ret=_this.apply(this,arguments); afterfn.apply(this,arguments); return ret; }; };
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();
使用裝飾者模式動態的改變ajax函數,傳輸的參數post
//是新添加的函數在舊函數以前執行 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'});
裝飾者模式分離表單驗證和提交的函數
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(); }
裝飾者模式和代理模式的區別:
代理模式的目的是,當直接訪問本體不方便或者不符合須要時,爲這個本體提供一個代替者。本體定義了關鍵功能,而代理提供了或者拒絕對他的訪問,或者是在訪問本體以前作一些額外的事情。
裝飾者模式的做用就是爲對象動態的加入某些行爲。