javascript設計模式(Alloy Team)

1.單例模式

單例模式的核心:
(1)確保只有一個實例
(2)提供全局訪問javascript

用代理實現單例模式:html

var ProxySingletonCreateDiv = (function(){
        var instance;
        return function( html ){
            if ( !instance ){
                instance = new CreateDiv( html );
            }
            return instance;
        }
    })();

通用的惰性單例模式:建立登錄懸浮窗java

//fn保存建立邏輯
//單例模式:只建立一次
 var getSingle = function( fn ){
        var result;
        return function(){
            return result || ( result = fn .apply(this, arguments ) );
        }
    };

    var createLoginLayer = function(){
        var div = document.createElement( 'div' );
        div.innerHTML = '我是登陸浮窗';
        div.style.display = 'none';
        document.body.appendChild( div );
        return div;
    };
    var createSingleLoginLayer = getSingle( createLoginLayer );
    document.getElementById( 'loginBtn' ).onclick = function(){
        var loginLayer = createSingleLoginLayer();
        loginLayer.style.display = 'block';
    };

2.發佈-訂閱者模式

(1)首先要指定好誰充當發佈者
(2)而後給發佈者添加一個緩存列表,用於存放回調函數以便通知訂閱者
(3)最後發佈消息的時候,發佈者會遍歷這個緩存列表,依次觸發裏面存放的訂閱者函數算法

var Event = (function(){
        var clientList = {},
        listen,
        trigger,
        remove;
        listen = function( key, fn ){
            if ( !clientList[ key ] ){
                clientList[ key ] = [];
            }
            clientList[ key ].push( fn );
        };
        trigger = function(){
            var key = Array.prototype.shift.call( arguments ),
            fns = clientList[ key ];
            if ( !fns || fns.length === 0 ){
                return false;
            }
            for( var i = 0, fn; fn = fns[ i++ ]; ){
                fn.apply( this, arguments );
            }
        };
        remove = function( key, fn ){
            var fns = clientList[ key ];
            if ( !fns ){
                return false;
            }
            if ( !fn ){
                fns && ( fns.length = 0 );
            }else{
                for ( var l = fns.length - 1; l >=0; l-- ){
                    var _fn = fns[ l ];
                    if ( _fn === fn ){
                        fns.splice( l, 1 );
                    }
                }
            }
        };
        return {
            listen: listen,
            trigger: trigger,
            remove: remove
        }
    })();

給全部的對象都動態安裝一個發佈-訂閱功能:緩存

var installEvent = function( obj ){
        for ( var i in event ){
            obj[ i ] = event[ i ];
        }
    };

3.裝飾着模式

(1)裝飾者模式能夠動態的給某個對象添加一些額外的職責,而不會影響從這個類中派生出的其餘對象。
(2)裝飾者也是包裝器:裝飾者模式將一個對象嵌入到另外一個對象中,實際上至關於這個對象被另外一個對象包裝起來,造成一條包裝鏈。app

3-1.在不改變原來函數的狀況下給函數增長新的功能dom

var a=function(){
        alert(1);
    }
    var _a = a;
    a=function(){
        _a();
        alert(2);
    }

可是這樣作會帶來兩個問題:必須維護_a這個中間變量;存在this被劫持的問題
3-2.用AOP裝飾函數函數

Function.prototype.before = function( beforefn ){
        var __self = this; // 保存原函數的引用
        return function(){ // 返回包含了原函數和新函數的"代理"函數
            beforefn.apply( this, arguments ); // 執行新函數,且保證this 不被劫持,新函數接受的參數
        // 也會被原封不動地傳入原函數,新函數在原函數以前執行
            return __self.apply( this, arguments ); // 執行原函數並返回原函數的執行結果,
        // 而且保證this 不被劫持
    }
}

Function.prototype.after = function( afterfn ){
    var __self = this;
    return function(){
        var ret = __self.apply( this, arguments );
        afterfn.apply( this, arguments );
        return ret;
    }
};
//調用:重寫原來函數
formSubmit = formSubmit.before( validata );

4.策略模式

策略模式的定義是:定義一系列的算法,把它們一個個封裝起來,而且使它們能夠相互替換。
4-1.使用策略模式計算獎金
績效爲S的人年終獎金是工資的4倍,績效爲A的人年終獎金是工資的3倍,績效爲B的人年終獎金是工資的2倍post

var performanceS = function(){};
    performanceS.prototype.calculate = function( salary ){
        return salary * 4;
    };
    var performanceA = function(){};
    performanceA.prototype.calculate = function( salary ){
        return salary * 3;
    };
    var performanceB = function(){};
    performanceB.prototype.calculate = function( salary ){
        return salary * 2;
    };

    //接下來定義獎金類Bonus:

    var Bonus = function(){
        this.salary = null; // 原始工資
        this.strategy = null; // 績效等級對應的策略對象
    };
    Bonus.prototype.setSalary = function( salary ){
        this.salary = salary; // 設置員工的原始工資
    };
    Bonus.prototype.setStrategy = function( strategy ){
        this.strategy = strategy; // 設置員工績效等級對應的策略對象
    };
    Bonus.prototype.getBonus = function(){ // 取得獎金數額
        return this.strategy.calculate( this.salary ); // 把計算獎金的操做委託給對應的策略對象
    };

    var bonus = new Bonus();
    bonus.setSalary( 10000 );

    bonus.setStrategy( new performanceS() ); // 設置策略對象
    console.log( bonus.getBonus() ); // 輸出:40000

4-2.javascript版本的策略模式this

var strategies = {
        "S": function( salary ){
            return salary * 4;
        },
        "A": function( salary ){
            return salary * 3;
        },
        "B": function( salary ){
            return salary * 2;

        }
    };
    var calculateBonus = function( level, salary ){
        return strategies[ level ]( salary );
    };

    console.log( calculateBonus( 'S', 20000 ) ); // 輸出:80000
    console.log( calculateBonus( 'A', 10000 ) ); // 輸出:30000

經過使用策略模式重構代碼,咱們消除了原來程序中大片的條件分支代碼。全部和計算獎金的邏輯再也不放在context中,而是分佈在各個策略對象中。每一個策略對象負責的算法已被各自封裝在對象內部。當咱們對這些策略對象發出「計算獎金」請求時,它們會返回各自不一樣的計算結果。

4-3.用策略模式進行表單校驗
(1)用戶名不能爲空
(2)密碼長度不能少於6位
(3)手機號碼必須符合格式

<html>
<body>
    <form action="http:// xxx.com/register" id="registerForm" method="post">
        請輸入用戶名:<input type="text" name="userName"/ >
        請輸入密碼:<input type="text" name="password"/ >
        請輸入手機號碼:<input type="text" name="phoneNumber"/ >
        <button>提交</button>
    </form>
    <script>
        /***********************策略對象**************************/
        var strategies = {
            isNonEmpty: function( value, errorMsg ){
                if ( value === '' ){
                    return errorMsg;
                }
            },
            minLength: function( value, length, errorMsg ){
                if ( value.length < length ){
                    return errorMsg;
                }
            },
            isMobile: function( value, errorMsg ){
                if ( !/(^1[3|5|8][0-9]{9}$)/.test( value ) ){
                    return errorMsg;
                }
            }
        };
        /***********************Validator 類**************************/
        var Validator = function(){
            this.cache = [];
        };
        Validator.prototype.add = function( dom, rules ){
            var self = this;
            for ( var i = 0, rule; rule = rules[ i++ ]; ){
                (function( rule ){
                    var strategyAry = rule.strategy.split( ':' );
                    var errorMsg = rule.errorMsg;
                    self.cache.push(function(){
                        var strategy = strategyAry.shift();
                        strategyAry.unshift( dom.value );
                        strategyAry.push( errorMsg );
                        return strategies[ strategy ].apply( dom, strategyAry );
                    });
                })( rule )
            }
        };
        Validator.prototype.start = function(){
            for ( var i = 0, validatorFunc; validatorFunc = this.cache[ i++ ]; ){
                var errorMsg = validatorFunc();
                if ( errorMsg ){
                    return errorMsg;
                }
            }
        };
        /***********************客戶調用代碼**************************/
        var registerForm = document.getElementById( 'registerForm' );
        var validataFunc = function(){
            var validator = new Validator();
            validator.add( registerForm.userName, [{
                strategy: 'isNonEmpty',
                errorMsg: '用戶名不能爲空'
            }, {
                strategy: 'minLength:6',
                errorMsg: '用戶名長度不能小於10 位'
            }]);
            validator.add( registerForm.password, [{
                strategy: 'minLength:6',
                errorMsg: '密碼長度不能小於6 位'
            }]);
            var errorMsg = validator.start();
            return errorMsg;
        }
        registerForm.onsubmit = function(){
            var errorMsg = validataFunc();
            if ( errorMsg ){
                alert ( errorMsg );
                return false;
            }

        };
    </script>
</body>
</html>

5.代理模式

代理模式是爲一個對象提供一個代用品或佔位符,以便控制對它的訪問。
5-1.小明追女神的故事
小明遇到了他的女神A,打算送一朵鮮花來表白。恰好小明打聽到他和A有一個共同的好友B,因而小明決定讓B來替本身來完成這件事。

var Flower = function(){};
var xiaoming = {
    sendFlower: function( target ){
        var flower = new Flower();
        target.receiveFlower( flower );
    }
};
var A = {
    receiveFlower: function( flower ){
        console.log( '收到花 ' + flower );
    }
};
xiaoming.sendFlower( A );


//接下來,咱們引入代理B,即小明經過B 來給A 送花:
var Flower = function(){};
var xiaoming = {
    sendFlower: function( target){
        var flower = new Flower();
        target.receiveFlower( flower );
    }
};
var B = {
    receiveFlower: function( flower ){
        A.receiveFlower( flower );
        90 第6 章 代理模式
    }
};
var A = {
    receiveFlower: function( flower ){
        console.log( '收到花 ' + flower );
    }
};
xiaoming.sendFlower( B );

//而後選擇A 心情好的時候把花轉交給A,代碼以下:

var Flower = function(){};
var xiaoming = {
    sendFlower: function( target){
        var flower = new Flower();
        target.receiveFlower( flower );
    }
};
var B = {
    receiveFlower: function( flower ){
        A.listenGoodMood(function(){ // 監聽A 的好心情
            A.receiveFlower( flower );
        });
    }
};

var A = {
    receiveFlower: function( flower ){
        console.log( '收到花 ' + flower );
    },
    listenGoodMood: function( fn ){
        setTimeout(function(){ // 假設10 秒以後A 的心情變好
            fn();
        }, 10000 );
    }
};

xiaoming.sendFlower( B );

var myImage = (function(){
    var imgNode = document.createElement( 'img' );
    document.body.appendChild( imgNode );
    return {
        setSrc: function( src ){
            imgNode.src = src;
        }
    }
})();

5-2.保護代理與虛擬代理
保護代理:好比B能夠幫助A過濾掉一些請求,如送花的人中年齡較大的
虛擬代理:new Flower多是很是昂貴的,這時候須要B代理去執行,代理B在A心情好的時候再執行。
5-3.虛擬代理實現圖片預加載

var myImage = (function(){
        var imgNode = document.createElement( 'img' );
        document.body.appendChild( imgNode );
        return function( src ){
            imgNode.src = src;
        }
    })();
    var proxyImage = (function(){
        var img = new Image;
        img.onload = function(){
            myImage( this.src );
        }
        return function( src ){
            myImage( 'file:// /C:/Users/svenzeng/Desktop/loading.gif' );
            img.src = src;

        }
    })();
    proxyImage( 'http:// imgcache.qq.com/music// N/k/000GGDys0yA0Nk.jpg' );

縱觀整個程序,咱們並無改變或者增長myImage接口,可是經過代理對象,其實是給系統添加了新的行爲:給img節點設置 src和圖片預加載這兩個功能。
5-4.虛擬代理合並http請求

<body>
    <input type="checkbox" id="1"></input>1
    <input type="checkbox" id="2"></input>2
    <input type="checkbox" id="3"></input>3
    <input type="checkbox" id="4"></input>4
    <input type="checkbox" id="5"></input>5
    <input type="checkbox" id="6"></input>6
    <input type="checkbox" id="7"></input>7
    <input type="checkbox" id="8"></input>8
    <input type="checkbox" id="9"></input>9
</body>

script.js:

var synchronousFile = function( id ){
        console.log( '開始同步文件,id 爲: ' + id );
    };

    var proxySynchronousFile = (function(){
        var cache = [], // 保存一段時間內須要同步的ID
        timer; // 定時器
        return function( id ){
            cache.push( id );
            if ( timer ){ // 保證不會覆蓋已經啓動的定時器
                return;
            }
            timer = setTimeout(function(){
            synchronousFile( cache.join( ',' ) ); // 2 秒後向本體發送須要同步的ID 集合
            clearTimeout( timer ); // 清空定時器
            timer = null;
            cache.length = 0; // 清空ID 集合
        }, 2000 );
        }
    })();

    var checkbox = document.getElementsByTagName( 'input' );
    for ( var i = 0, c; c = checkbox[ i++ ]; ){
        c.onclick = function(){
            if ( this.checked === true ){
                proxySynchronousFile( this.id );
            }
        }
    };
相關文章
相關標籤/搜索