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

聲明:這個系列爲閱讀《JavaScript設計模式與開發實踐》 ----曾探@著一書的讀書筆記javascript

裝飾者模式的定義:

裝飾者(decorator)模式可以在不改變對象自身的基礎上,在程序運行期間給對像動態的添加職責。與繼承相比,裝飾者是一種更輕便靈活的作法。java

裝飾者模式的特色:

能夠動態的給某個對象添加額外的職責,而不會影響從這個類中派生的其它對象;git

繼承的一些缺點:

  1. 繼承會致使超類和子類之間存在強耦合性,當超類改變時,子類也會隨之改變;github

  2. 超類的內部細節對於子類是可見的,繼承經常被認爲破壞了封裝性;ajax

傳統面向對象的裝飾者和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的指向發生變化,致使執行結果發生變化。app

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函數

在須要執行的函數以後執行某個新添加的功能函數post

//新添加的函數在舊函數以後執行
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. 裝飾者模式的做用就是爲對象動態的加入某些行爲。


相關文章
相關標籤/搜索