js设计模式(基于coder的肤浅的设计模式探究):
单例模式,单体模式,工厂模式,策略模式,模版模式,观察-订阅者模式,外观模式
理解js的设计模式,你会发现一些比较深层次的东西,会体会到js发明者的用心良苦和循序渐进,一门语言在刚出来的时候难免会有一些比较明显的反人类的设计,但是在不断的实践中,这些设计反而越来越受人们推崇,这就是计算机语言的魅力所在,我们在项目中的每走一步都会无形中用到一种设计模式,这些模式潜移默化的存在在我们的js代码块中,一个程序员如果能从设计模式角度去看待一门语言的话,
说明你真正热爱这门语言,想去弄懂这门语言的一些基本思想,基本设计理念,js的所有设计模式都有一个共同的目的,那就是让编程更加模块化,系统化,明确化,无论是哪种设计模式,我们相信,存在即合理。
一、发布-订阅模式(Vue的双向绑定原理就是基于此模式)
发布订阅模式的流程如下:
1. 确定谁是发布者(一般是一个对象)【Vue中此模式的对象应该是实例化的vue对象本体】。
2. 然后给发布者添加一个缓存列表,用于存放回调函数来通知订阅者(多个对象)【Vue中的多个对象应该是绑定此数据的所有有关的标签元素和js对象】。
3. 发布消息,发布者需要遍历这个缓存列表,依次触发里面存放的订阅者回调函数,此回调函数
作用是用来告知订阅者,发布者发布了新消息,会触发每一个订阅者对应的回调函数,来告知
每一个订阅者【Vue中,数据发生改变后我们通过此方法来通知我们的订阅者列表里面的相关订阅者,以此来触发回调函数里面的代码块】。
4、退订(比如不想再接收到这些订阅的信息了,就可以取消掉)【Vue中不存在订阅者退订现象】
此模式提供三个方法,来确保完成整个流程
发布消息方法(发布者执行)
订阅者订阅方法(订阅者执行)
订阅者退订方法(订阅者执行)
二、单体模式(常用的防止变量污染的设计模式)
单体模式比较好理解,它是一个对象,我们创建这个对象的目地是为了系统管理代码中的一些公用的变量,对象,函数,避免出现变量污染,一般我们声明一些变量和函数的时候,我们是在封闭的函数中,一般不会造成变量污染情况,但是以防万一,原因是在js中,全局变量和局部变量的关系比较复杂(有时候还夹杂着一部分变量提升问题),不少coder经常会因为此问题出现bug老半天找不到原因,创建好这个对象以后我们只是对外暴露一个对象入口,使用里面的变量时我们可以以object.
变量名的形式来调用变量,其实也是为了实现js代码块的划分命名空间来设计的。
var Singleton = {
attribute:true,
method1:function(){},
method2:function(){}
};
三、单例模式(常用的单例模式实例是弹出框)
单例模式的概念,是指单个对象,并且此对象有且只有一个实例化对象,保证一个类只有一个
对应的实例化对象,在实例化前会进行判断,如果已经有了实例化对象直接return,否则才会去实例化
对象出来,这样做是为了保证一些逻辑性的交互的存在,举个简单的栗子,登陆弹出框,假如我们现在
要做一个登陆弹出框,我们在点击登陆按钮的时候,弹出框要弹出来,我们再次点击按钮,并不会再次
弹出一个登录框(不排出有些奇葩会去做弹出多个登录框,让用户一个一个去关闭的反人类交互),要
想实现单例模式,并不轻松,我们可以使用闭包来实现。常用的单例模式是全局本地缓存,
var single = (function(){
var unique;
function getInstance(){
// 如果该实例存在,则直接返回,否则就对其实例化
if( unique === undefined ){
unique = new Construct();
}
return unique;
}
function Construct(){
// ... 生成单例的构造函数的代码
}
return {
getInstance : getInstance
}
})();
四、策略模式(遵循对修改关闭,对扩展开放的原则)
策略模式主要是将算法的使用和算法的实现分离开,便于代码的维护,避免经常性的修改源代码,
只需要添加新代码即可,举个栗子,超市卖东西,vip0.5折,老用户0.8折,新用户没有折扣,
不使用策略模式,我们只能写一个条件判断语句来区分用户身份,
function Price(personType, price) {
//vip 5 折
if (personType == 'vip') {
return price * 0.5;
}
else if (personType == 'old'){ //老客户 3 折
return price * 0.3;
} else {
return price; //其他都全价
}
}
这么写看似没有什么问题,但是如果我们要添加用户身份呢,或者我们要频繁去修改折扣呢,我们需要
不断的修改if else中的判断条件了,
使用了策略模式,看似代码量多了
// 对于vip客户
function vipPrice() {
this.discount = 0.5;
}
vipPrice.prototype.getPrice = function(price) {
return price * this.discount;
}
// 对于老客户
function oldPrice() {
this.discount = 0.3;
}
oldPrice.prototype.getPrice = function(price) {
return price * this.discount;
}
// 对于普通客户
function Price() {
this.discount = 1;
}
Price.prototype.getPrice = function(price) {
return price ;
}
// 上下文,对于客户端的使用
function Context() {
this.name = '';
this.strategy = null;
this.price = 0;
}
Context.prototype.set = function(name, strategy, price) {
this.name = name;
this.strategy = strategy;
this.price = price;
}
Context.prototype.getResult = function() {
console.log(this.name + ' 的结账价为: ' + this.strategy.getPrice(this.price));
}
var context = new Context();
var vip = new vipPrice();
context.set ('vip客户', vip, 200);
context.getResult(); // vip客户 的结账价为: 100
var old = new oldPrice();
context.set ('老客户', old, 200);
context.getResult(); // 老客户 的结账价为: 60
var Price = new Price();
context.set ('普通客户', Price, 200);
context.getResult(); // 普通客户 的结账价为: 200
策略模式最实用的场合就是某个“类”中包含有大量的条件性语句,比如if...else 或者 switch。每一个条件分支都会引起该“类”的特定行为以不同的方式作出改变。以其维护一段庞大的条件性语句,不如将每一个行为划分为多个独立的对象。每一个对象被称为一个策略。设置多个这种策略对象,可以改进我们的代码质量,也更好的进行单元测试
五、外观模式(使用最频繁的一种模式)
外观模式其实就是定义一个函数,通过传递一些必要的参数,我们可以在函数内部封装一些比较复杂的操作当需要通过一个单独的函数或方法来访问一系列的函数或方法调用,以简化代码库的其余内容,使得代码更容易跟踪管理或者更好的维护时,可以使用外观模式。其实我们平时代码中这种模式应该是用的比较多的。