經常使用js設計模式整理

在作canvas、webGL遊戲時,很深切的感受到,遊戲編程玩的都是設計模式。架構沒預先設計好,強耦合性代碼在後期維護簡直災難。javascript

大型單頁應用裏,複雜度上升到必定程度時,沒有適當的設計模式進行降耦,後續的開發也難如下手。java

而設計模式正是爲了降耦而存在。web

參考《javascript設計模式》——曾探算法

函數節流

var throttle = function(fn, interval){
    var _self = fn,
        timer,
        firstTime = true;

    return function(){
        var args = arguments.
            _me = this;
        
        if( firstTime ){
                _self.apply(_me, args);
            return first
            }
            
            if(timer){
            return false;
        }
    
        timer = setTimeout(function(){
            clearTimeout(timer);
            timer = null;
            _self.apply(_me, args);
        }, interval || 500);
    }    
}

window.onresize = throttle(function(){
    console.log(1);
}, 500);

分式函數

var timeChunk = function(arr, fn, count){
    var obj, t;
    var len = ary.length;

    var start = function(){
        for(var i = 0; i < Math.min(coutn||1, arr.length); i++){
            var obj = ary.shift();
            fn(obj);
        }
    }

    return function(){
        t = setInterval(function(){
            if(ary.length === 0){
                return clearInterval(t);
            }
            start();
      
        }, 200);
    };
};

惰性函數(重寫自重寫)

var addEvent = function(elem, type, handler){
if(window.addEventListener){
    addEvent = function(elem, type, handler){
        elem.addEventListener(type, handler, false);
    }
} else if(window.addachEvent){
    addEvent = function(elem, type, handler){
        elem.attachEvent('on' + type, handler);   
    }
}

}編程

單例模式

定義:保證類有且僅有一個實力,並提供全局訪問接口。canvas

私有變量加下劃線 var _a = 1;

通用惰性單例模式

把建立單例和管理單例的職責分開來:設計模式

var getSingle = function(fn){
    var result;
    return function(){
        return result || (result = fn.apply(this, arguments));
    }
}

var createLayer = function(){
    var div = document.createElement('div');
    document.body.appendChild(div);
}

var createSingleCreateLayer = getSingle(createLayer);

document.getElementById('#test').onclick = () => {
    createSingleCreateLayer();
}

策略模式

定義:封裝一系列算法,並使其能夠相互替換緩存

計算工資Demo:性能優化

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

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

表單添加多種校驗規則:架構

/*--------------- Strategies --------------*/
var strategies = {
    isNotEmpth: (value, errorMsg) => {
        if(value == ''){
            return errorMsg;
        }
    },
    minLength: (value, length,errorMsg) => {
        if(value.length < length){
            return errorMsg;
        }
    }
}

/*---------------- Validator ---------------*/
var Validator = () => {
    this.cache = [];
}

Validator.prototype.add = (dom, rules) => {
    var self = this;
    
    for(var i = 0; rule; rule = rules[i++]){
        ((rule) => {
            var strategyAry = 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 = () => {
    for(var i = 0; validatorFunc; validatorFunc = this.cache[i++]){
        var errorMsg = validatorFunc();
        
        if( errorMsg ){
            return errorMsg;
        }
    }
}

/*-------------- 客戶端調用 ------------*/
var registerForm = document.getElementById('registerForm');

var validataFunc = () => {
    var validator = new Validator();
    
    validator.add(registorForm.username, [{
        strategy: 'isNotEmpty',
        errorMsg: '用戶名不能爲空'
    },{
        strategy: 'minLength',
        errorMsg: ‘最下長度爲6’
    }]);
    
    validator.add(registorForm.password, [{
        strategy: 'minLength',
        errorMsg: '最小長度'
    }])
    
    var errorMsg = validator.start();
    return errorMsg;
}

registerForm.onsubmit = () => {
    var errorMsg = validataFunc();
    
    if(errorMsg){
        alert(errorMsg);
        return false;
    }
}

代理模式

爲一個對象提供一個代用品或者佔位符,以便控制它的訪問

面向對象設計原則——單一職責原則。面向對象設計鼓勵將行爲分佈到細粒度的對象之中。

大量計算時加入代理做爲緩存,異步請求時同理可用緩存:

var mult = () => {
    var a = 1;
    for(var i = 0, l = arguments.length; i < l; i++){
        a = a * arguments[i];
    }
    return a;
}

var proxyMult = (() => {
    var cache = {};
    return () => {
        var args = Array.prototype.join.call(arguments, ',');
        if( args in cache ){
            return cache[args];
        }
        
        return cache[args] = mult.apply(this, arguments);
    }
})();

高階函數動態建立代理

var mult = () => {
    var a = 1;
    for(var i = 0, l = arguments.length; i < l; i++){
        a = a * arguments[i];
    }
    return a;
}

var plus = () => {
    var a = 0;
    for(var i = 0, l = arguments.length; i < l; i++){
        a = a + arguments[i];
    }
    return a;
}

var createProxyFactory = (fn) => {
    var cache = {};
    return () => {
        var args = Array.prototype.join.call(arguments, ',');
        if(args in cache){
            return cache[args];
        }
        
        return cache[args] = fn.apply(this, arguments);
    }
}

迭代器模式

迭代器模式指提供一種方法順序訪問一個聚合對象的各個元素,又不暴露該對象的內部表示。

通用外部迭代器:

var Iterator = (obj) => {
    var current = 0;
    
    var next = () => {
        ++current;
    }
    
    var isDone = () => {
        return current >= obj.length;
    }
    
    var getCurrItem = () => {
        return obj[current];
    }
    
    return {
        next: next,
        isDone: isDone,
        getCurrItem: getCurrItem
    }
}

發佈-訂閱模式

var event = {
    clientListh: [],
    listen: function(key, fn){
        if(!this.clientListh[key]){
            this.clientList[key] = [];
        }
        this.clientList[key].push(fn);
    },
    trigger: function(){
        var key = Array.prototype.shift.call(arguments),
            fns = this.clientList[key];
            
        if(!fns || fns.length === 0){
            return false;
        }
        
        for(var i = 0; fn; fn = fns[i++]){
            fn.apply(this.arguments);
        }
    }
}

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

var salesOffices = {};
installEvnet(salesOffices);

salesOffices.listen('squareMeter88', function(price){
    console.log('price': + price);
})

salesOfffices.trigger('squareMeter88', 20000);

發佈——訂閱模式能夠很大程度下降耦合性,但濫用也會形成背後邏輯混亂,且浪費內存。

命令模式

主要是回調函數的的面向對象的替代品

沒看懂命令模式有什麼意義

撤銷和重作

var commandStack = [];
document.onkeypress = (ev)=>{
    var keyCode = ev.keyCode,
        command = makeCommand(Ryu, commands[keyCode]);
        
    if(command){
        command();
        commandStack.push(command);
    }
}

$('#replay').onclick = ()=>{
    var command;
    while(command = commandStack.shift()){
        command();
    }
}

宏命令

var closeDoorCommand = {
    excute: ()=> {
        console.lg('close door');
    }
}

var openPcCommand = {
    excute: ()=> {
        console.log('open pc');
    }
}

var MacroCommand = ()=> {
    return {
        conmandList: [],
        add: (command)=>{
            this.commandList.push(command);
        },
        execute: ()=>{
            for(var i = 0, command; command = this.commandsList[i++]; ){
                command.execute();
            }
        }
    }
}

組合模式

相似命令模式的增強版,也是看不懂深層意義

遍歷文件夾

/************ Folder ****************/
var Folder = (name)=>{
    this.name = name;
    this.parent = null;
    this.files = [];
};

Folder.prototype.add = (file)=> {
    file.parent = this;
    this.files.push(file);
}

Folder.prototype.scan = ()=>{
    console.log('Begin scan: ' + this.name);
    for(var i = 0, file = this.files; file = files[i++]; ){
        file.scan();
    }
}

Folder.prototype.remove = ()=>{
    if(!this.parent){
        return;
    }
    
    for(var files = this.parent.files, l = files.length-1; l >= 0; l--){
        var file = files[l];
        if(file === this){
            files.splice(l, 1);
        }
    }
}



/************ File ****************/
var File = (name)=>{
    this.name = name;
    this.parent = null;
}

File.prototype.add = ()=>{
    throw new Error('Can not add file under file');
}

File.prototype.scan = ()=>{
    console.log('Begin Scan: ' + this.name);
}

File.prototype.remove = ()=>{
    if(!this.parent){
        return;
    }
    for(var files = this.parent.files, l = files.length-1; l >= 0; l--){
        var file = files[l];
        if(file == this);{
            file.splice(l, 1);
        }
    }
}

模板方法模式

基於繼承的設計模式

模板方法模式友抽象父類和具體實現的子類組成。父類封裝了子類的算法框架,子類經過集成抽象類,也繼承了整個算法框架。

鉤子方法

var Bevrage = function(){}

Beverage.prototype.boilWater = function(){
    console.log('把水煮沸');
}
Beverage.prototype.brew = function(){
    throw new Error('Class brew musth be rewrited');
}

....

Beverage.prototype.addCondiments = function(){
    throw new Error('adCondiments must be rewrited');
}

Beverage.prototype.customerWantsCondiments = function(){
    return true;
}

Beverage.prototype.init = function(){
    this.boilWater();
    this.brew();
    ....
    if(this.customerWantsCondiments()){
        this.addCondiments();
    }    
}

var CoffeeWithHook = function(){};

CoffeeWithHook.prototype = new Beverage();

CoffeeWithHook.prototype.brew = function(){
    console.log('brew coffee with water');
};

CoffeeWithHook.prototype.customerWantsCondiments = function(){
    return widow.confirm('須要調料嘛?');
}

var coffeeWithHook = new CoffeeWithHook();
coffeeWithHook.init();

高階函數能夠更方便的實現上面的demo...

享元模式

用於性能優化,核心是運用共享技術來支持大量細粒度的對象。

通用對象池的實現

var objectPoolFactory = (createObjFn)=>{
    var objectPool = [];
    
    return{
        create: function(){
            var obj = objectPool.length === 0 ? createObjFn.apply(this, arguments) : objectPool.shift();
            
            return obj;
        },
        recover: (obj){
            objectPool.push(obj);
        }
    }
}

職責鏈模式

用來重構代碼挺方便的。

把不一樣功能函數包裝成鏈式節點再調用。

var order500 = function(orderType, pay, stock){
    if(orderType == 1 && pay == true){
        conosle.log('500, 100優惠券')
    } else{
        return 'nextSuccessor'; // 把請求日後傳遞
    }    
}

var order200 = function(orderTYPE, pay, stock){
    if(orderType == 2 && pay ==true){
        console.log('200, 50優惠券');
    } else{
        return 'nextSuccessor';
    }
}

var orderNormal = function(orderType, pay, stock){
    if(stock > 0){
        console.log('普通購買,無優惠券');
    } else{
        console.log('庫存不足');
    }
}

//職責鏈包裝函數
//Chain.prototype.setNextSuccessor 指定在鏈中的下一個結點
//Chain.prototype.passRequest 請求傳遞給某個結點

var Chain = function(fn){
    this.fn = fn;
    this.successor = null;
}

Chain.prototype.setNextSuccessor = function(successor){
    return this.successor = seccussor;
}

Chain.prototype.passRequest = function(){
    var ret = this.fn.apply(this, arguments);
    
    if(ret === 'nextSuccessor'){
        return this.successor && this.seccessor.passRequest.apply(this.successor, arguments);
    }
    
    return ret;
}

//將訂單函數包裝進職責鏈

var chainOrder500 = new Chain(order500);
var chainOrder200 = new Chain(order200);
var chainOrderNormal = new Chain(orderNormal);

chainOrder500.setNextSuccessor(chainOrder200);
ChainOrder200.setNextSuccessor(chainOrderNormal);

//Test]
chainOrder500.pasRequest(1, true, 500);

中介者模式

我的感受有點像代理模式.用一箇中介對象,來處理其餘對象的時間,以實現解耦的目的。

但缺點也很明顯,當系統複雜到必定程度時,中介者對象慢慢會變成一個難以維護的對象

裝飾者模式

動態的給類添加職責

相關文章
相關標籤/搜索