浅谈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>

查看代码运行的结果

HTML jquery 流程设计器 jquery设计模式_html

3.观察者模式

观察者模式是定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。我认为的观察者模式是这样:

HTML jquery 流程设计器 jquery设计模式_html_02


也是这样:

HTML jquery 流程设计器 jquery设计模式_html_03


观察者模式的目的是,给一个对象添加监视,当它的状态发生改变的时候,通知监视它的其他对象,就像上课玩手机,被上课老师知道,也被班主任老师知道这样的情况,下面就以此为例,写一个观察者模式。

<!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>

我们运行上面的代码得到的结果是

HTML jquery 流程设计器 jquery设计模式_观察者模式_04

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有没有给自己帮忙找这本书

HTML jquery 流程设计器 jquery设计模式_HTML jquery 流程设计器_05


看,达到了我们的目的!!!!

小结

这是笔者对常见的四种(笔者将观察者模式和订阅/发布模式分开来讲)设计模式的粗浅认识,如果有不足或错误之处,请大家多加指正,感谢支持,希望能和大家共同进步。