一、单例模式
1、定义
只有一个实例,可以全局访问。
主要解决:一个全局使用的类,频繁的创建和销毁
何时使用:当想控制实例的数目,节省系统化资源的时候
如何实现:判断系统是否已经有这个单例,如果有则返回,没有则创建
单例模式优点:内存中只有一个实例,减少了内存的开销,尤其是频繁地创建和销毁实例,比如首页页面的缓存
2、案例
实现一个登陆的弹窗
<html>
<button id="button">登陆</button>
</html>
<script>
//一般写法
var createLoginLayer = (function(){
var div
return function(){
if(!div){
div=document.createElement('div')
div.innerHTML = '我是登陆弹窗'
div.style.display = 'none'
document.body.appendChild(div)
}
return div
}
})()
document.getElementById('button').onclick = function(){
var loginLayer = createLoginLayer()
loginLayer.style.display="block"
}
//单例模式的写法
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 createSingleLogin = getSingle (createLoginLayer )
document.getElementById('button').onclick = function(){
var loginLayer = createSingleLogin ()
loginLayer.style.display="block"
}
</script>
3、es6实现单例模式
...js
class LyEDU{
constructor(name,creator,products){
this.name = name;
this.creator = creator;
this.products = products;
}
static getInstance(name,creator,products){
if(!this.instance){
this.instance = new LyEDU(name,creator,products)
}
return this.instance
}
}
var LyCom = LyEDU.getInstance('1','2',['3','4'])
var LyComCom = LyEDU.getInstance('5','6',['31','42'])
console.log(LyCom === LyComCom) //true
...
二、策略模式
1、定义
定义一系列的算法,把他们封装起来,并且他们之间可以相互替换
核心:将算法的使用和算法的实现分离开来
2、案例
绩效为S的人年终奖有4倍工资,绩效为A的人员年终奖有3倍工资,绩效为B的年终奖有2倍工资
var strategies = {
"S":function(salary){
return salary * 4
},
"A":function(salary){
return salary * 3
},
"B":function(salary){
return salary * 2
},
}
var getBounds = function(level,salary){
return strategies[level](salary)
}
console.log(getBounds('S',10000))
3、表单验证策略模式
一般代码:
...html
<form action="XXX.com" method="post" id="registerForm">
请输入用户名:<input type="text" name="username">
请输入密码:<input type="password" name="password">
请输入手机号码:<input type="text" name="phonenumber">
</form>
...
...js
var registerForm = document.getElementById('registerForm');
registerForm.onsubmit = function(){
if(registerForm.username.value == ''){
alert('用户名不能为空')
return false
}
if(registerForm.password.value.length < 6){
alert('密码不能少于6位')
return false
}
if(!/^1[3|5|7|8][0-9]{9}$/.test(registerForm.phonenumber.value)){
alert('手机号码格式不正确')
return false
}
}
...
策略模式的代码
var registerForm = document.getElementById('registerForm');
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|8|5][0-9]{9}$/.test(value)){
return errorMsg
}
}
}
//假设用户按照如下使用已经封装好的验证
var validateFun = function(){
var validator = new Validator();
validator.add(registerForm.username,'isNonEmpty','用户名不能为空')
validator.add(registerForm.password,'mingLength:6','密码的长度不能小于6位')
validator.add(registerForm.phonenumber,'isMobile','手机号码格式不正确')
var errorMsg = validator.start()
return errorMsg
}
registerForm.onsubmit = function(){
var errorMsg = validateFun()
if(errorMsg){
alert(errorMsg)
return false
}
}
// 参照假设的使用,封装验证类
var Validator = function(){
this.cache = [];
}
Validator.prototype.add = function(dom,rule,errorMsg){
var ary = rule.split(':')
this.cache.push(function(){
var stategy = ary.shift();
ary.unshift(dome.value);
ary.push(errorMsg)
return strategies[stategy](...ary)
//或者 return strategies[stategy].apply(dom,ary)
})
}
Validator.prototype.start = function(){
for(var i=0,vaFunc;vaFunc = this.cache[i++];){
var msg = vaFunc();
if(msg){
return msg
}
}
}
三、发布订阅模式
1、含义
发布订阅模式又称观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知
2、作用
支持简单地广播通信,当对象状态发生改变,会自动通知已经订阅过的对象;
可以应用在异步编程中,替代回调函数,可以订阅ajax之后的事件,只需要订阅自己需要的部分
对象之间的松耦合,两个对象之间都互相不了解彼此,但是,不影响通信,当所有的订阅者出现的时候,发布的代码无需改变,同样发布的代码改变,只要之前约定的名称没有改变,也不影响订阅
发布订阅者模式封装的代码如下:
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
}
})()
实际代码使用如下
var a =(function(){
var count = 0;
var button = document.getElementById('count')
button.onclick = function(){
Event.trigger('add',count++)
}
})()
var b = (function(){
var div = document.getElementById('showcount')
Event.listen('add',function(count){
div.innerHTML = count
})
})()
3、缺点:创建订阅者需要消耗一定的时间和内存;虽然可以弱化对象之间的联系,如果过度使用的话,反而使代码不好理解、不好维护等