设计模式听起来像很是高端的样子,其实呢,确实很提莫难啊啊啊啊。言归正传,所谓的设计模式也就是一些比较优秀的编程思想。有了这些思想加于利用到实际开发中,可以减少工作量(便于维护)。废话不多说直接进入主题。
工厂模式
工厂模式,顾名思义,就是为了创造对象。也就是一些公共方法???
工厂模式最重要的优点是:可以在父类实现一些相同的方法,而具体要实现的业务逻辑可以放在子类中,通过子类重写父类的方法,去实现自己的业务逻辑,减少冗余代码。
简单工厂模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function CarFactory (brand, price) { var car = new Object(); car.brand = brand; car.price = price; car.getPrice = function () { return this.price; } return car; } var car1 = CarFactory("牌子A", 10000); var car2 = CarFactory("牌子B", 20000); console.log(JSON.stringify(car1)); console.log(JSON.stringify(car2)); console.log(typeof car1); console.log(typeof car2); console.log(car1 instanceof Object);
|
函数CarFactory接受两个参数brand, price,最终返回一个对象。如果多次调用这个函数,每次将返回一个新的对象,这就跟工厂的生产线一样。
复杂工厂模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| function ComplexCarFactory(brand, price) { this.brand = brand; this.price = price; } ComplexCarFactory.prototype = { constructor: ComplexCarFactory, sellCar: function(){ var speed = this.getSpeed(this.brand); console.log(this.brand + '的车子售价:' + this.price + '元人民币,限速' + speed + '公里每小时'); }, getSpeed : function(brand){ throw new Error("父类是抽象类不能直接调用,需要子类重写该方法"); } }; var CarChild = function(brand, price) { this.brand = brand; this.price = price; ComplexCarFactory.call(this, brand, price); }; CarChild.prototype = Object.create(ComplexCarFactory.prototype); CarChild.prototype.getSpeed = function(brand){ var speed = null; if(brand === '牌子C'){ return 100; } return 50; } var car3 = new CarChild("牌子C", 3000); console.log(car3); console.log(car3.sellCar());
|
ComplexCarFactory为父类,CarChild为子类,CarChild继承自ComplexCarFactory。
ComplexCarFactory不在进行对象实例化,只对创建过程中的一般性问题进行处理,ComplexCarFactory就像是Java中的抽象类,必须被子类重写,否则调用ComplexCarFactory的getSpeed方法时就会抛出异常。
CarChild继承自ComplexCarFactory,同时重写了父类的方法,CarChild类实例后的对象之间是相互独立的,具体的业务逻辑会放在子类中进行编写。
单例模式
单例模式思想在于保证一个特定类仅有一个实例,意味着当你第二次使用同一个类创建信对象时,应得到和第一次创建对象完全相同。
单例模式在我们平时的应用中用的比较多的,相当于把我们的代码封装在一个起来,只是暴露一个入口,从而避免全部变量的污染。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| var Singleton = function(name){ this.name = name; }; Singleton.prototype.getName = function(){ return this.name; }
var getInstance = (function() { var instance = null; return function(name) { if(!instance) { instance = new Singleton(name); } return instance; } })();
var a = getInstance("aa"); var b = getInstance("bb"); console.log(b.getName()); console.log(a === b);
|
实现一个单例模式,无非就是使用一个变量来标识该类是否被实例化,如果未被实例化的话,那么我们可以实例化一次,否则的话,直接返回已经被实例化的对象。
模块模式
模块模式是为单例模式添加私有变量和私有方法,并减少全局变量的使用;
使用场景,创建一个对象时,需要进行内部初始化,同时对内部属性跟方法有访问权限限制,就需要使用模块模式了。
如下就是一个模块模式的代码结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| var singleMode = (function(){ var privateNum = 112; function privateFunc(){}, function publicMethod1(){}, function publicMethod2(){}, return { publicMethod1: publicMethod1, publicMethod2: publicMethod2 }; })();
|
模块模式使用了一个返回对象的匿名函数。在这个匿名函数内部,先定义了私有变量和函数,供内部函数使用,然后将一个对象字面量作为函数的值返回,返回的对象字面量中只包含可以公开的属性和方法。这样的话,可以提供外部使用该方法;由于该返回对象中的公有方法是在匿名函数内部定义的,因此它可以访问内部的私有变量和函数。
代理模式
代理模式的优点在于:代理对象可以代替本体对象被实例化,此时本体对象未真正实例化,等到合适时机再实例化。代理模式可以延迟创建开销很大的本体对象,他会把本体的实例化推迟到有方法被调用时。
图片加载
使用图片是非常常见的场景,如果直接给img标签设置src属性,如果图片过大,或网速比较慢,图片在加载过程中会有一段时间的空白,用户体验不好。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| var myImage = (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(){ myImage.setSrc(this.src); }; return { setSrc: function(src) { myImage.setSrc("http://img.lanrentuku.com/img/allimg/1212/5-121204193R0.gif"); img.src = src; } } })();
ProxyImage.setSrc("https://www.baidu.com/img/bd_logo1.png");
|
myImage 函数只负责做一件事,创建img元素加入到页面中,其中的加载loading图片交给代理函数ProxyImage 去做,当图片加载成功后,代理函数ProxyImage 会通知及执行myImage 函数的方法,同时当以后不需要代理对象的话,我们直接可以调用本体对象的方法即可。
缓存代理
对第一次运行的结果进行缓存,当再一次运行相同运算的时候,直接从缓存里面取,避免重复运算,如果运算非常复杂的话,对性能很耗费,那么使用缓存对象可以提高性能。以下是一个简单的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| var mult = function(){ var a = 1; for(var i = 0,ilen = arguments.length; i < ilen; i+=1) { a = a*arguments[i]; } return a; };
var plus = function(){ var a = 0; for(var i = 0,ilen = arguments.length; i < ilen; i+=1) { a += arguments[i]; } return a; }
var proxyFunc = function(fn) { var cache = {}; return function(){ var args = Array.prototype.join.call(arguments,','); if(args in cache) { return cache[args]; } return cache[args] = fn.apply(this,arguments); } }; var proxyMult = proxyFunc(mult); console.log(proxyMult(1,2,3,4)); console.log(proxyMult(1,2,3,4));
var proxyPlus = proxyFunc(plus); console.log(proxyPlus(1,2,3,4)); console.log(proxyPlus(1,2,3,4));
|
职责链模式
策略模式
一、策略模式可以有效避免很多if条件语句 二、策略模式符合开放-封闭原则,使代码更容易理解和扩展 三、策略模式中的代码可以复用.
表单检验是非常常见的功能。因为涉及到大量的验证规则,使用策略模式会非常便利。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| var strategys = { isNotEmpty: function(value,errorMsg) { if(value === '') { return errorMsg; } }, minLength: function(value,length,errorMsg) { if(value.length < length) { return errorMsg; } }, mobileFormat: function(value,errorMsg) { if(!/(^1[3|5|8][0-9]{9}$)/.test(value)) { return errorMsg; } } }; 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 strategys[strategy].apply(dom,strategyAry); }); })(rule); } }; Validator.prototype.start = function(){ for(var i = 0, validatorFunc; validatorFunc = this.cache[i++]; ) { var msg = validatorFunc(); if(msg) { return msg; } } };
var registerForm = document.getElementById("registerForm"); var validateFunc = function(){ var validator = new Validator(); validator.add(registerForm.userName,[ {strategy: 'isNotEmpty',errorMsg:'用户名不能为空'}, {strategy: 'minLength:6',errorMsg:'用户名长度不能小于6位'} ]); validator.add(registerForm.password,[ {strategy: 'minLength:6',errorMsg:'密码长度不能小于6位'}, ]); validator.add(registerForm.phoneNumber,[ {strategy: 'mobileFormat',errorMsg:'手机号格式不正确'}, ]); var errorMsg = validator.start(); return errorMsg; };
registerForm.onsubmit = function(){ var errorMsg = validateFunc(); if(errorMsg){ alert(errorMsg); return false; } }
|
发布-订阅模式
发布—订阅模式又叫观察者模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知。
eg:假设小🐶看上了一双鞋子,但该鞋子已经断货了,卖家承诺她到货通知。与此同时,小🐷、小🐒也关注了这双鞋子。
在这个场景中,卖家就是发布者,小🐶,🐷,🐒等人都属于订阅者。当鞋子到货时,会依次通知到每个人。
实现:
1.首先要想好谁是发布者(比如上面的卖家)。
2.然后给发布者添加一个缓存列表,用于存放回调函数来通知订阅者(比如上面的买家收藏了卖家的店铺,卖家通过收藏了该店铺的一个列表名单)。
3.最后就是发布消息,发布者遍历这个缓存列表,依次触发里面存放的订阅者回调函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| var Event = (function(){ var list = {}, listen, trigger, remove; listen = function(key,fn){ if(!list[key]) { list[key] = []; } list[key].push(fn); }; trigger = function(){ var key = Array.prototype.shift.call(arguments), fns = list[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 = list[key]; if(!fns) { return false; } if(!fn) { fns && (fns.length = 0); }else { for(var i = fns.length - 1; i >= 0; i--){ var _fn = fns[i]; if(_fn === fn) { fns.splice(i,1); } } } }; return { listen: listen, trigger: trigger, remove: remove } })();
Event.listen("color",function(size) { console.log("尺码为:"+size); }); Event.trigger("color",42);
|