什么是设计模式?
针对特定的问题,给出简介而优化的方案,在特点的情况,特定的时期,针对特点的问题使用的。
单例模式?
在何种情况下会使用单例模式呢?
当你想要一个构造函数,一辈子只能new一个对象的时候使用
使用场景:
每个网页有肯定要有很多弹出框,如果不使用单例模式的话,每new就是创造一个div,这样会导致代码冗余和内存溢出,如果使用单例模式,每次new的都是第一次div,只是文字内容改变了。
核心代码:
var Person = (function fn(){
function Student(){
this.name = "pcy"
}
Student.prototype.say = function(){console.log("hello world")}
let instance = null
return function fn1(){
return !instance? instance = new Student() : instance
}
})()
let p = new Person()
let p1 = new Person()
console.log(p);//Student {name: "pcy"}
console.log(p1);//Student {name: "pcy"}
console.log(p === p1);//true
p.say()//hello world
p1.say()//hello world
我们来分析一下:
正常情况下,console.log(p === p1)应该返回false,但是如果我们使用单例模式,他们就返回true,因为他们new的都是同一个对象,使用自执行函数,是为了延长instance的生命周期,Person的是fn函数执行以后的返回值,即fn1函数,fn1第一次执行会得到一个实例,以后的每一次执行得到的都是第一次的时候,里面的逻辑就是判断他有没有instance,如果没有的话就返回他一个实例,赋值给instance,有的话就返回instance,即那个实例,所以每次new的都是同一个实例。
组合模式
什么是组合模式?
把多个对象组成树状结构来表示局部与整体,使得用户可以同时操作单个对象和对象的组合。
使用场景:
比如一个页面中,有一个轮播图,你切换页面了,轮播图的dom已经不动了,但是定时器还在动,所以我们需要关闭定时器,但是一个页面肯定不止一个轮播图,我们不可能给每个轮播图都关闭定时器,这个时候我们就可以给他们设一个总开关,去控制他们。这就是组合模式的使用场景
实现组合模式方式:
有若干个启动方式一样的构造函数,我们准备一个总开关,只要总开关一启动,这些构造函数都启动
+ 需要一个承载所有构造函数的实例数组
+ 需要一个方法,向数组里面添加内容
+ 需要一个方法,把所有数组里面的内容启动了
代码实现:
class Play{
constructor(){}
init(){
console.log("我在打游戏");
}
}
class Sleep{
constructor(){}
init(){
console.log("我在睡觉");
}
}
class Eat{
constructor(){}
init(){
console.log("我在吃饭")
}
}
class Compose{
constructor(){
this.arr = []
}
add(instance){
this.arr.push(instance)
}
init(){
console.log("总开关启动了");
this.arr.forEach(item=>item.init())
}
}
//masteroff就是总开关
let masteroff = new Compose()
//每次执行添加方法 就往总开关上加内容
masteroff.add(new Sleep())
masteroff.add(new Eat())
masteroff.add(new Play())
//只要总开关启动,里面的构造函数就启动了
masteroff.init() //====> 总开关启动了
// 开始玩游戏了
// 开始吃东西
// 开始睡觉
观察者模式
什么是观察者模式?
观察者模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动更新。观察者模式属于行为型模式,行为型模式关注的是对象之间的通讯,观察者模式就是观察者和被观察者之间的通讯
使用场景:
vue
案例:初中班主任就是观察者,我们自己就是被观察者,对于观察者:当被观察者的状态被改变了,他们就会触发技能,请家长,对于被观察者:我们的状态就应该是学习,当我们状态发生改变,就会立马通知正在观察里的人,触发技能
代码实现
class Student{
constructor(){
//定义初始状态
this.state="好好学习"
this.observers=[]//放观察者的,将来如果状态改变,就会通知这个数组里的人,触发技能
}
//改变自己的状态
setState(value){
this.state = value
this.notify()
}
//获取自己的状态
getState(){
return this.state
}
//添加观察者
add(observer){
this.observers.push(observer)
}
//通知观察者,状态改变了,触发技能
notify(){
this.observers.forEach(item=>{item.qingjiazhang(this.state)})
}
}
class Observer{
constructor(name){
this.name = name
}
qingjiazhang(state){
console.log(`我是${this.name},因为你${state}了,所以明天叫家长来学学校`);
}
}
//创建一个被观察的实例
let xiaoming = new Student()
//创建三个观察者的实例
let banzhuren = new Observer("班主任")
let nianjizhuren = new Observer("年纪主任")
let xiaozhang = new Observer("校长")
//让班主任 年纪主任 观察者小明
xiaoming.add(banzhuren)
xiaoming.add(nianjizhuren)
//改变小明的状态
xiaoming.setState("玩手机")
来分析一下:
让班主任和年纪主任观察者小明,(通过小明.add方法添加观察者)当小明的状态从好好学习变成,玩手机,就触发notify方法,遍历观察者数组,触发观察者的请家长技能,把改变的状态传递下去,最后打印
订阅 发布模式
什么是订阅 发布 模式?
定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知。
使用场景:
addEventListener(click,fn)
理解订阅 发布模式:
我去书店买书,管理员告诉我这个书要过几天才出版,我说好的到了电话通知我一下,这里我就订阅了,当出版社出版书的时候,管理员就会打电话告诉我书到了你可以来买书了。这里就是出版社发布了,管理员知道了告诉我。 我向管理员订阅了这本书,当出版社发布的时候,和管理员才会告诉我书到了
实现方式:
1.消息盒子{}
=>click:[fn1,fn2,fn3]
=>mouseover:[fn4,fn5,fn6]
2.订阅的方法
=>向消息盒子里面添加内容
3.取消订阅的方法
=>把已经订阅的方法,从消息盒子内部拿走
4.发布事件的方法
=>把消息盒子里面的对应的处理函数执行了
代码实现
class Observer{
constructor(){
this.message={}
}
//订阅的方法
on(type,fn){//type 类型 fn:事件处理函数
if(!this.message[type]){//如果message对象里没有这个,就新建一个数组
this.message[type] = []
}
this.message[type].push(fn)//如果没有就新建一个数组,事件处理函数加进去
}
//取消订阅的方法
delete(type,fn){
//如果消息中心为空,就直接返回,不然的话就过滤数组,item和fn不一样的
if(!this.message[type]) return
this.message[type] = this.message[type].filter(item => item !== fn)
}
//发布的方法
out(type,...arg){
//判断有没有定位过,没有订阅过直接跳过
if(!this.message[type]) return
var event = {
type:type,
data:arg
}
this.message[type].forEach(item => item(event))
}
}
let p = new Observer()
//订阅事件
p.on('click',handlerA)
p.on('click',handlerB)
p.on('click',handlerC)
//取消订阅事件
p.delete('click',handlerA)
//发布事件
p.out('click','hello world',true,{name:'jack'})
//准备几个事件处理函数
function handlerA(e){console.log('我是事件处理函数,handerA',e)}
function handlerB(e){console.log('我是事件处理函数,handerB',e)}
function handlerC(e){console.log('我是事件处理函数,handerC',e)}