1. 程式人生 > >JavaScript設計模式----裝飾者模式

JavaScript設計模式----裝飾者模式

宣告:這個系列為閱讀《JavaScript設計模式與開發實踐》 —-曾探@著一書的讀書筆記

裝飾者模式的定義:

裝飾者(decorator)模式能夠在不改變物件自身的基礎上,在程式執行期間給對像動態的新增職責。與繼承相比,裝飾者是一種更輕便靈活的做法。

裝飾者模式的特點:

可以動態的給某個物件新增額外的職責,而不會影響從這個類中派生的其它物件;

繼承的一些缺點:

  1. 繼承會導致超類和子類之間存在強耦合性,當超類改變時,子類也會隨之改變;
  2. 超類的內部細節對於子類是可見的,繼承常常被認為破壞了封裝性;

傳統面向物件的裝飾者和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. 裝飾者模式的作用就是為物件動態的加入某些行為。

圖片名稱