js常用的几种设计模式
设计模式就是针对特定问题,给出的简洁而优化的处理方案
常用的设计模式
- 单例模式
- 组合模式
- 观察者模式
…
1. 单例模式
- 单例模式就是,使构造函数只有一个实例对象,也就是一个构造函数只能new出一个对象
- 单例模式的核心代码
<script>
function Person(){
this.name='Jack'
}
//全局
var instance=null;
function singleton(){
if(instance===null){
instance=new Person()
}
return instance
}
/*
singleton第一次调用
=>if条件判断,判断instance===null
=>singleton自己的作用域里面没有instance,就到父级作用域访问
=>到父级作用域(全局作用域)拿到了instance
=>现在全局的instance就是一个null,if条件为true
=>给nstance赋值一个Person的实例(实例001)
=>现在全局的instance的值就是实例001
=>return instance就是return实例001
=>p1得到的就是实例001
*/
var p1=singleton()
/*
singleton第二次调用的时候
=>if条件判断,判断instance===null
=>singleton自己的作用域里面没有instance,就到父级作用域访问
=>到父级作用域(全局作用域)拿到了instance
=>现在全局的instance就是一个实例001
=>if条件不满足,代码不执行
=>return instance就是return实例001
=>p2得到的依旧是实例001
*/
var p2=singleton()
//p1和p2得到的都是第一次执行的时候的实例,p1和p2是同一个地址
console.log(p1===p2)//true
<script/>
- 单例模式的作用
如果构造函数生成的实例化对象、属性、功能、方法都相同,就生成一个实例化对象
2. 单例模式
- 组合模式就是把几个构造函数的入口函数组合在一起,然后使用一个‘遥控器’进行统一调用
- 实现组合模式
需要一个数组来存放所有构造函数的实例
需要一个方法,往数组里面添加内容
需要一个方法,来启动数组里面的 所有内容 - 组合模式的作用
通过一个方法,控制多个构造函数的执行
3. 观察者模式
- 观察者模式也称发布/订阅模式或者消息模式
- 作用
主体数据改变,通知其他相关个体,作出相应的数据更新 - 从两个角度分析观察者模式
观察者角度(观察者和发布/订阅这两个是一个设计模式)
- 观察者:就像学校管理学生一样,有班主任、年级主任、教务主任,他们都有一个共同的技能就是请家长。他们就是在暗中观察学生的人,一旦学生做的事情违反了校纪校规,就会触发他们请家长的技能
- 被观察者:学生就是被观察者,学生的状态就是好好学习,一旦学生的状态改变为早恋,立马就会通知正在观察学生的观察者,观察者就会触发他们的技能
- 目的:让观察者看着被观察者,只要数据改变了,就让观察者做一些事情
//准备两个构造函数
//1.被观察者(学生)
class Student{
constructor(){
//学生本身的状态,就是好好学习
this.state='好好学习'
//准备一个数组,存放观察学生的观察者
//一旦学生的状态从好好学习变成早恋的时候,就让这个数组里面的所有老师触发技能
this.observers=[]
}
//方法
//1.可以改变状态的方法
setState(value){
//可以改变状态
this.state=value
//状态一旦改变,就要通知观察者
this.notify()
}
//2.获取状态
getState(){
return this.state
}
//3.添加观察者
//往this.observers里面追加一个观察者
attach(observer){
this.observers.push(observer)
}
//4.通知this.observers数组里面的每一个观察者,学生状态改变了
notify(){
this.observers.forEach(item => item.qingjiazhang(this.state))
}
}
//2.观察者(老师)
class Observer{
constructor(name){
//标明观察者是班主任还是年级主任还是教务主任
this.name=name
}
//方法,就是老师能触发的技能
qingjiazhang(state){
console.log(`我是${this.name},因为你${state}了,我要请你家长来!`);
}
}
var xiaoming=new Student();//被观察者
//观察者
var banzhuren=new Observer('班主任');
var nianjizhuren=new Observer('年级主任');
var jiaowuzhuren=new Observer('教务主任');
//让班主任看着小明
xiaoming.attach(banzhuren);
//让年级主任看着小明
xiaoming.attach(nianjizhuren);
//让教务主任看着小明
xiaoming.attach(jiaowuzhuren);
//当小明状态改变的时候,xiaoming.setState()就能改变小明现在的状态
//就触发xiaoming.notify()
//就会把xiaoming.observers这个数组里面的每一名老师的请家长技能调用
xiaoming.setState('早恋');//对象调用方式
发布/订阅角度(观察者和发布/订阅是两个设计模式)
- 分三个状态
订阅
取消订阅
发布 - 发布/订阅需要的内容
- 消息盒子(click:[fn1,fn2,fn3] mouseover:[fn4,fn5,fn6])
- 订阅的方法(往消息盒子里面添加内容)
- 取消订阅的方法(把已经订阅的方法从消息盒子内部拿走)
- 发布事件的方法(把消息盒子里面对应的处理函数执行了)
例子:买书
我想要买一本《不曾孤独,怎会懂得》的书,我去到书店找,书店没有,我就店员说一声,等有了就打电话通知我=>订阅事件
我在家等店员的通知,一旦书店到货,店员就给我打电话 =>店员发布事件
在等待期间,朋友就送了我这本书,我就告诉店员,我不需要这本书了,不用打电话给我了=>取消订阅事件
class Observer{
constructor(){
//1.准备消息盒子
this.message={}
}
//2.订阅的方法
on(type,fn){
//往消息盒子里面添加内容
//type是事件类型,你要订阅的类型
//fn是事件处理函数,你要给这个类型订阅一个什么行为
//假设现在的type==='click'
//消息盒子里面就应该是{click:[handlerA]}
//如果消息盒子里面没有click这个成员,那么就创建这个成员是一个空数组,然后添加进去
//如果消息盒子里面有这个click成员,那么就直接push
if(!this.message[type]){
//表示this.message里面没有这个成员
this.message[type]=[]
}
this.message[type].push(fn)
}
//3.取消订阅的方法
off(type,fn){
//删除消息盒子里面的某一个成员
//type是要取消的事件类型,fn是要取消的事件处理函数
//先看看有没有这个事件类型,如果没有,不需要取消,有才需要取消
if(!this.message[type]) return
//this.message[type]是一个数组,取消这个数组里面和fn一样的事件处理函数
//使用数组常用方法filter
this.message[type]=this.message[type].filter(item =>item !== fn)
//this.message[type]=this.message[type].filter(function(item){
//return item !==fn;
//})
}
//4.发布的方法
emit(type,...arg){//arg=>arguments
//执行消息盒子里面某一个对应的处理函数
//type就是要触发的事件类型
//判断有没有订阅过,如果没有订阅过,就直接return
if(!this.message[type]) return
//组装一个事件对象
var event={
type:type,
//参数
data:arg
}
//调用每一个事件处理函数的时候,都带上一个事件对象
this.message[type].forEach(item =>item(event))
}
}
//将来需要使用的时候
var o=new Observer()
//订阅事件
o.on('click',handlerA)
o.on('click',handlerB)
o.on('click',handlerC)
//发布的事件
o.emit('click','hello world',100,true,{name:'Jack'})
//事件处理函数
function handlerA(e){console.log('我是事件处理函数handlerA',e)}
function handlerB(e){console.log('我是事件处理函数handlerB',e)}
function handlerC(e){console.log('我是事件处理函数handlerC',e)}
function handlerD(e){console.log('我是事件处理函数handlerD',e)}
function handlerE(e){console.log('我是事件处理函数handlerE',e)}