浅谈js中常见的设计模式
近几天学习了几个常用到的设计模式,自己总结了一下,加上了自己的理解和看法,下面对几个常用的设计模式逐一分析
1.单例设计模式
从名字我们就可以看出,单例就是指一个实例的意思,我们知道,构造函数实例化出来的对象,虽然长得一样,但是他们实际上是不同的对象,比如下面代码所示:
function Person(){
='Tom'
}
let p1=new Person();
let p2=new Person();
console.log(p1===p2) //false
从上面的代码我们可以看到,通过构造函数new出来的实例对象是两个不同的对象,如果我们想要每次new出来的实例对象是相同的一个对象即共用一个空间地址,达到console.log(p1===p2)结果是true的效果,就需要用到单例模式,下面我们来看看单例模式是怎么实现的。
<script type="text/javascript">
//单例模式的目的:让一个构造函数只能new出一个对象,后面的对象不管怎么new,都是一个实例对象,都指向一个地址
function Person(){
='Tom'
}
//定义一个全局变量
let str=null;
//单例模式的核心代码
function Example(){
if(str===null){
str=new Person()
}
return str
}
//实例化一个对象
let e1=new Example();
let e2=new Example();
console.log(e1===e2)//true
</script>
当我们用一个全局变量保存了第一次创建的实例对象的地址后,第二次的new对象,就会通过变量str直接获取到第一次保存的实例对象,从而达到两次得到的实例对象是同一个的目的。
这个代码看起来不是我们那种构造函数new出来的结果,我们把这个过程包装一下,让这些代码看起来和我们常见的构造函数创建对象的方法一样。
<body>
<!-- 通过自执行函数(function () {})(),变化代码的执行 -->
<script type="text/javascript">
//用一个变量接受返回的函数,变量名自定义
let Person=(function fn(){
function Person (){
='Tom'
}
let str=null;
return function(){
return !str?str=new Person():str
}
})()
let p1=new Person();
let p2=new Person();
console.log(p1===p2)//true
</script>
</body>
这样看起来就和我们用构造函数创建对象的方法一样啦!!!
2.组合模式
什么组合模式,我们可以这样理解,想象这样一个场景,当你下班回到家的时候,打开进门走廊那里的一个开关,家中客厅、厨房、卧室的灯都会打开。走廊里面的灯是一个总开关,当这个灯打开时就会把绑定在它身上的其他开关也打开,这就是一个组合模式。
组合模式的目的:把我们若干这启动方式一样的构造函数放在一起,准备一个总开关, 总开关一启动, 那么这些构造函数就都启动。
<script type="text/javascript">
//1.准备一个总开关
class Compose{
constructor(){
//下面这个数组用来存和 ‘总开关’ 绑在一起的其他 ‘开关’
this.composeArr=[]
}
//向数组里面添加‘开关’的方法
add(instance){
this.composeArr.push(instance)
}
// ‘总开关’ 打开,其余的 ‘开关’ 也打开
init0(){
console.log('总开关打开')
//让数组中的每一个元素都执行各自的init()方法
this.composeArr.forEach(item=>item.init());
}
}
//2.准备第一个‘开关’
class Open1{
constructor() {}
init(){
console.log('我是开关1');
}
}
//3.准备第二个‘开关’
class Open2{
constructor() {}
init(){
console.log('我是开关2')
}
}
//4.第三个‘开关’
class Open3{
constructor() {}
init(){
console.log('我是开关3')
}
}
let c1=new Compose();
//5.在 ‘总开关’ 里面添加其他 ‘开关’
c1.add(new Open1());
c1.add(new Open2());
c1.add(new Open3());
//6.打开‘总开关’
c1.init0(); //所有的开关都打开了
</script>
查看代码运行的结果
3.观察者模式
观察者模式是定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。我认为的观察者模式是这样:
也是这样:
观察者模式的目的是,给一个对象添加监视,当它的状态发生改变的时候,通知监视它的其他对象,就像上课玩手机,被上课老师知道,也被班主任老师知道这样的情况,下面就以此为例,写一个观察者模式。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<!-- 例子:学生的状态改变了,就将他转变状态告诉老师1,老师2。。。。。。 -->
<script type="text/javascript">
// 一.首先定义一个 学生(被观察者)的构造函数
class Student{
constructor() {
// 定义一个状态
this.state='学习状态';
//创建一个用来储存老师1、2(观察者)的数组
this.observers=[]
}
//1.定义一个改变自己状态的方法
setState(value){
this.state=value;
//状态改变,此处又执行通知观察者的方法
this.notice()
}
//2.定义一个获取自己状态的方法
getState(){
return this.state
}
//3.添加观察者的方法
attach(observer){
this.observers.push(observer)
}
//4.执行通知观察者的方法
notice(){
//每一个观察者都会被通知并且执行他们共同的技能(know),知道此时被观察者的状态
this.observers.forEach(item=>item.know(this.state))
}
}
//二,创建第二个构造函数,这些用来new观察者,也就是 创建老师1、2
class Observer{
constructor(name) {
=name
}
//所有的观察者都有一个知道(know)的方法
know(state){
console.log(`我是${},我知道你现在的状态是${state}`)
}
}
//检验
//1.新建一个被观察者
let student1=new Student();
//2.创建两个观察者
let teacher1=new Observer('老师1');
let teacher2=new Observer('老师2');
//3.将这两个观察者加到用来存观察者的数字里面,用student的attach方法
student1.attach(teacher1);
student1.attach(teacher2);
//4.设置被观察者的状态,用student的setState方法设置
student1.setState('看手机')
student1.setState('发呆')
</script>
</body>
</html>
我们运行上面的代码得到的结果是
4.订阅/发布模式
很多人认为观察者模式和订阅/发布模式是一个模式,笔者按照自己的理解,把他们分为两个模式比较。构成订阅/发布模式,我们可以这么理解。
举个栗子
你需要买一本《JS从入门到放弃》这本书,首先我们明确自己的目标,买到这本书,然后,我们先去商店A,商店A老板说没有这本书,你会给他说,让他帮忙买到这本书。然后你又去商店B,商店B的老板也说没有,你也告诉他,让他帮忙给你买到这本书。如何实现这个效果,我们看看代码。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script type="text/javascript">
//自定义了几个函数,相当于我们想要买的书
function f1(){console.log('急收书1')};
function f2(){console.log('急收书2')};
function f3(){console.log('急收书3')};
//1.创建一个构造函数
class Observer{
constructor(){
//定义一个属性用来存商家A和B
this.message={}
}
//1.用来告诉老板(订阅)
on(type,fn){
//判断有没有让商店A、B老板帮忙
if(!this.message[type]){
this.message[type] = []
}
this.message[type].push(fn)
}
//2.定义一个取消订阅的方法(如果我们已经告诉了商店A老板帮我们买,就不用重复告诉他了。如果我们已经买到了,就告诉他不用给我们买了)
off(type,fn){
if(!this.message[type]) return
this.message[type] = this.message[type].filter(item => item !== fn)
console.log('执行了取消订阅的方法')
}
//3.增加一个发布的方法(商店老板想办法给你弄到你要的书)
publish(type){
//判断你有没有给商家A、B老板说
if(!this.message[type]) return
//说了(订阅)过就执行他们
this.message[type].forEach(item => item())
}
}
//实例化检验
let new1=new Observer();
//我们可以认为book1和book2是商店A和B
//f1,f2,f3是你想要的三本书
//下面的代码就是告诉商店A和B你想要的书
new1.on("book1",f1)
new1.on("book1",f2)
new1.on("book2",f1)
new1.on("book2",f2)
new1.on("book2",f3)
//取消一个订阅事件(你通过其他途径得到了书f3,你告诉商店B老板这本书你不需要了)
new1.off('book2',f3)
// console.log(new1)
//发布
//商店A和B帮你发布消息
new1.publish('book1')
new1.publish('book2')
</script>
</body>
</html>
我们来看看执行的结果,看看商店A和B有没有给自己帮忙找这本书
看,达到了我们的目的!!!!
小结
这是笔者对常见的四种(笔者将观察者模式和订阅/发布模式分开来讲)设计模式的粗浅认识,如果有不足或错误之处,请大家多加指正,感谢支持,希望能和大家共同进步。