前言
前段时间找工作,在一次笔试过程中碰到过手写实现发布订阅者模式,后来回来总结了这三种设计模式的前端实现和应用。
提示:如有不对,请多多指教
一、单例模式
定义:1 、只有一个实例 2 、可以全局的访问
主要解决:一个全局使用的类 频繁的创建和销毁
何时使用:想控制实例的数量 节省系统化资源的时候
如何实现:判断系统是否已经有这个单例 如果有则返回 没有就创建
单例模式优点:内存中只有一个实例 ,减少内存的开销,尤其是频繁的创建和销毁实例 (比如首页页面的缓存)
使用场景:1、全局的缓存 2 弹窗
ES5实现单例模式
需求 实现一个登陆的弹窗
// 1、页面加载完成的时候已经创建好这个弹窗,一开始弹窗是隐藏的状态,弹窗出现
// 缺点:资源浪费
var loginLayer = (function(){
var div = document.createElement('div')
div.innerHTML = '我是登录的弹窗'
div.style.display = 'none'
document.body.appendChild(div)
return div
})()
document.getElementById('button').onclik = function (){
loginLayer.style.display = 'block'
}
// 2、点击创建
// 缺点:频繁创建和销毁
// 3、单例模式
// 判断系统是否已经有这个单例模式 如果有则返回 没有则创建
// 有一个标记在内存中做一个标记,不能被销毁,使用闭包实现
var loginLayer = (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').onclik = function (){
var loginLayer = loginLayer()
loginLayer.style.display = 'block'
}
// 单一职责思想进行改进单例:
//单例职责
var getSingle = function(fn){
var result
return function(){
return result || (result = fn.apply(this,arguments))
}
}
//创建登录框的职责
var loginLayer = function(){
var div = document.createElement('div')
div.innerHTML = '我是登录的弹窗'
div.style.display = 'none'
document.body.appendChild(div)
return div
}
var creatSingleLogin = getSingle (loginLayer)
document.getElementById('button').onclik = function (){
var loginLayer = creatSingleLogin()
loginLayer.style.display = 'block'
}
ES6实现单例模式
1、无类的语言
2、Class类可以看做是es5构造函数的语法糖
// (1)、 es5构造函数:方法直接定义在prototype上
function person(name,sex){
this.name = name
this.sex = sex
}
person.prototype.say = function(){
conosole.log(‘shazi’)
}
let person1 = new person(‘星空’,’男’)
// (2)、Es6实现:使用class类,class里面的方法实际也是定义在prototype
class person{
//constructor方法在实际开发过程中更多是做初始化的操作
constructor(name,sex){
this.name = name
this.sex = sex
}
say(){
conosole.log(‘shazi’)
}
}
Let person1 = new person(‘星空’,’男’)
// (3)、Es6实现单例模式:静态方法static定义的方法只能被类本身调用,不能被实例调用
class person{
//constructor方法在实际开发过程中更多是做初始化的操作
constructor(name,sex){
this.name = name
this.sex = sex
}
static Say(name,sex){
if(!this.instance){
this.instance = new person(name,sex)
}
return this.instance
}
}
Let person1 = person.Say(‘星空’,’男’)
Let person2 = person.Say(‘星空’,’男’)
二、策略模式
定义一系列的算法 把他们封装起来 并且他们之间可以相互替换
核心:将算法的使用和算法的实现分离开来
1、函数也是对象,策略模式封装算法
var strategies = {
'S':function(salary){
return salary * 4
},
'A':function(salary){
return salary * 3
},
'B':function(salary){
return salary *2
},
}
var getBouns = function (level,salary){
return strategies [level](salary)
}
2、表单验证
常规写法
<form action=”xxx.com” method=”post” id=”registerForm”>
请输入用户名:<input type=”text” name=”username”>
请输入密码:<input type=”passwodrd” name=”passwodrd”>
请输入电话号码:<input type=”text” name=”phonenumber”>
<button>提交</button>
</form>
var registerFrom = document.getElementById(‘registerForm’)
registerFrom .onsubmit = function(){
if(!registerFrom .username.value == ‘’){
alert(‘用户名不能为空’)
return false
}
if(!registerFrom .passwodrd.value.length < 6){
alert(‘密码长度不能小于6’)
return false
}
if(!/^1[3|5|8][0-9]{9}$/.test(registerFrom .username.value)){
alert(‘手机号码格式不正确’)
return false
}
}
运用策略模式 ,封装表单验证(实现分成三部分:策略对象 一系列算法 一系列业务逻辑)
// 策略对象
var strategise = {
isNonEmpty:function(value,errorMsg){
if(value == ‘’){
return errorMsg
}
}
minLength:function(value,length,errorMsg){
if(value.length < 6){
return errorMsg
}
}
isMobile:function(value,errorMsg){
if(!/^1[3|5|8][0-9]{9}$/.test(value)){
return errorMsg
}
}
}
// 一系列业务逻辑 (假设存在验证类 Validator new Validator())
var registerFrom = document.getElementById('registerForm')
var validateFun = function(){
var validator = new Validator()
//添加验证规则
validator.add(registerFrom.username,'isNonEmpty','用户名不能为空')
validator.add(registerFrom.password,'minLength','密码长度不能小于6')
validator.add(registerFrom.phonenumber,'isMobile','手机号码格式不正确')
//开启验证
var errorMsg = validator.start()
return errorMsg
}
registerFrom.onsubmit = function (){
var errorMsg = validateFun()
if(errorMsg ){
alert(errorMsg )
return false
}
}
// 一系列算法(封装策略类 构造函数 class)**
var validator = function (){
//保存验证规则的数组
this.cache = []
}
validator.prototype.add = function (dom, rule, errorMsg){
var ary = rule.aplit(“:”)
this.cache.push(function(){
var strategy = ary.shift()//用户选择的验证规则
ary.unshift(dom.value)
ary.push(errorMsg)
return strategies[strategy].apply(dom,ary)
//return strategies[strategy](...ary) //ary数组作为参数传递
})
}
validator.prototype.start= function (){
for(var i=0,vaFunc;vaFunc=this.cache[i++]){
var msg = vaFunc()
if(msg){
return msg
}
}
}
三、发布订阅者模式
实现思路:
1、首先要想好谁是发布者(比如谁是卖家)
2、然后给发布者添加一个缓存列表,用于存放回调函数来通知订阅者(比如买家收藏了卖 家的店铺,卖家拥有收藏了该店铺的一个列表名单)
3、最后就是发布消息,发布者遍历这个缓存列表,依次触发里面存放的订阅者回调函数
var Event = (function(){
var list ={},//缓存列表
listen,//订阅者
trigger,//发布者
remove;//取消订阅
listen = function(key,fn){
if(!list[key]){
list[key] = []
}
list[key].push(fn)
}
trigger = function(){
//对应的key取出来,arguments伪数组,接收传入函数的所有参数
var key = Array.prototype.shift.call(arguments)//取出第一个参数
var fns = list[key]
if(!fns || fns.length == 0){
return
}
for(var i=0;fn;fn=fns[i++]){//发布给订阅者
fn(...arguments)
}
}
remove = function(key,fn){
var fns = list[key]
if(!fns){
return false
}
if(!fn){
//短路表达式、逻辑与 && 的运算方式,左边的值布尔转换后为true,那么返回 //右边的值
fn && (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,trigger,remove}
})()
解决问题1:
实现低耦合,改进异步操作中的强耦合,比如登录成功后返回的信息在不能模块的应用
解决问题2:
vue实现跨组件之间的传值
发布者父组件:
name:'我师父组件',
clickButton(){
pubsub.trigger('item',this.name)
}
订阅者子组件:
msg:'',
let that = this
pubsub.listen('item',function(data){
that.Msg = data
})
总结
一直觉得笨一点没关系,只要我保持学习,总有一天会飞起来。