Javascript 常用设计模式

186 阅读5分钟

Javascript设计模式

设计模式的定义:在面向对象软件设计过程中针对特定问题的简洁而优雅的解决方案

单例模式

单例模式的定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。实现的方法为先判断实例存在与否,如果存在则直接返回,这就确保了一个类只有一个实例对象。

适用场景:一个单一对象。比如:弹框,无论点击多少次,弹框只应该被创建一次。

    class CreateModal {
        constructor(name) {
            this.name = name;
            this.getName();
        }
        getName() {
            return this.name;
        }
    }
    
    // 代理实现单例模式
    var ProxyMode = (function() {
        var instance = null;
        return function(name) {
            if(!instance) {
                instance = new CreateModal(name);
            }
            return instance;
        }
    })()
    // 测试单体模式的实例
    var a = new ProxyMode("modal1");
    var b = new ProxyMode("modal2");
    // 因为单体模式是只实例化一次,所以下面的实例是相等的
    console.log(a === b);    //true

策略模式

策略模式的定义:定义一系列的算法,把他们一个个封装起来,并且使他们可以互相替换。

策略模式的目的就是将算法的使用和算法的实现分离开来。

一个基于策略模式的程序至少由两部分组成。第一部分是一组策略类(可变),策略类封装了具体的算法,并负责具体的计算过程。第二部分是环境类Context(不变),Context接受客户的请求,随后将请求委托给一个策略类。要做到这一点,说明Context中要维持对某个策略对象的引用。

    /*策略类*/
    var levelOBJ = {
        "A": function(money) {
            return money * 4;
        },
        "B"function(money) {
            return money * 5;
        }
        ,
        "C"function(money) {
            return money * 6;
        }
    }
    /*环境类*/
    var calculateBouns =function(level,money) {
        return levelOBJ[level](money);
    };
    console.log(calculateBouns('A',10000)); // 40000

代理模式

代理模式的定义:为一个对象提供一个代用品或占位符,以便控制对它的访问

常见的虚拟代理形式:某一个花销很大的操作,可以通过虚拟代理的方式延迟到这种需要它的时候才去创建(例:使用虚拟代理实现图片懒加载)

图片懒加载的方式:先通过一张loading图占位,然后通过异步的方式加载图片,等图片加载好了再把完成的图片加载到img标签里面。

    var imgFunc = (function() {
        var imgNode = document.createElement('img');
        document.body.appendChild(imgNode);
        return {
            setSrc: function(src) {
                imgNode.src = src;
            }
        }
    })();
    var proxyImage = (function() {
        var img = new Image();
        img.onload = function () {
            imgFunc.setSrc(this.src);
        }
        return {
            setSrc: function(src) {
                imgFunc.setSrc('./loading.gif');
                img.src = src;
            }
        }
    })();
    proxyImage.setSrc('./a.png');

使用代理模式实现图片懒加载的优点还有符合单一职责原则。减少一个类或方法的粒度和耦合度。

中介者模式

中介者模式的定义:通过一个中介者对象,其他所有的相关对象都通过该中介者对象来通信,而不是相互引用,当其中的一个对象发生改变时,只需要通知中介者对象即可。通过中介者模式可以解除对象与对象之间的紧耦合关系。

例如:现实生活中,航线上的飞机只需要和机场的塔台通信就能确定航线和飞行状态,而不需要和所有飞机通信。同时塔台作为中介者,知道每架飞机的飞行状态,所以可以安排所有飞机的起降和航线安排。 中介者模式适用的场景:例如购物车需求,存在商品选择表单、颜色选择表单、购买数量表单等等,都会触发change事件,那么可以通过中介者来转发处理这些事件,实现各个事件间的解耦,仅仅维护中介者对象即可。

    var goods = {   //手机库存
    'red|32G': 3,
    'red|64G': 1,
    'blue|32G': 7,
    'blue|32G': 6,
};
//中介者
var mediator = (function() {
    var colorSelect = document.getElementById('colorSelect');
    var memorySelect = document.getElementById('memorySelect');
    var numSelect = document.getElementById('numSelect');
    return {
        changed: function(obj) {
            switch(obj){
                case colorSelect:
                    //TODO
                    break;
                case memorySelect:
                    //TODO
                    break;
                case numSelect:
                    //TODO
                    break;
            }
        }
    }
})();
colorSelect.onchange = function() {
    mediator.changed(this);
};
memorySelect.onchange = function() {
    mediator.changed(this);
};
numSelect.onchange = function() {
    mediator.changed(this);
};

装饰者模式

装饰者模式的定义:在不改变对象自身的基础上,在程序运行期间给对象动态地添加方法。

例如:现有4种型号的自行车分别被定义成一个单独的类,如果给每辆自行车都加上前灯、尾灯、铃铛这3个配件,如果用类继承的方式,需要创建4*3=12个子类。但如果通过装饰者模式,只需要创建3个类。

Function.prototype.before = function(beforefn) {
    var self = this;    //保存原函数引用
    return function(){  //返回包含了原函数和新函数的 '代理函数'
        beforefn.apply(this, arguments);    //执行新函数,修正this
        return self.apply(this,arguments);  //执行原函数
    }
}
Function.prototype.after = function(afterfn) {
    var self = this;
    return function(){
        var ret = self.apply(this,arguments);
        afterfn.apply(this, arguments);
        return ret;
    }
}
var func = function() {
    console.log('2');
}
//func1和func3为挂载函数
var func1 = function() {
    console.log('1');
}
var func3 = function() {
    console.log('3');
}
func = func.before(func1).after(func3);
func();

发布订阅模式

它其实是一种对象间一对多的依赖关系(不是综艺节目以一敌百那种),当一个对象的状态发送改变时,所有依赖于它的对象都将得到状态改变的通知

    
      class Event {
        constructor() {
          this.list = {};
        }
        on(key, fn) {
          if (!this.list[key]) {
            this.list[key] = [];
          }
          this.list[key].push(fn);
        }
        emit() {
          let key = new Array().shift.call(arguments),
            fns = this.list[key];
          if (!fns || fns.length === 0) {
            return false;
          }
          console.log(fns);
          fns.forEach(fn => {
            fn.apply(this, arguments);
          });
        }
        remove(key, fn) {
          let fns = this.list[key];
          if (!fns) return false;
          // 如果没有传对应函数的话
          // 就会将key值对应缓存列表中的函数都清空掉
          if (!fn) {
            fns && (fns = []);
          } else {
            // 遍历缓存列表,看看传入的fn与哪个函数相同
            // 如果相同就直接从缓存列表中删掉即可
            fns.forEach((cb, i) => {
                console.log(cb,fn)
              if (cb === fn) {
                fns.splice(i, 1);
              }
            });
          }
        }
      }
      function dog() {
        console.log("一起旺旺旺");
      }
      let event = new Event();
      event.on("pet", data => {
        console.log("接收数据");
        console.log(data);
      });
      event.on("pet", () => {
        console.log("dog");
      });
      event.on("pet", dog);
      // 取消dog方法的订阅
      event.remove("pet", dog);
      event.emit("pet", "aa1");