js设计模式学习笔记
1、单例模式
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>单例模式</title> 7 </head> 8 <body> 9 10 </body> 11 <script> 12 /* 13 单例模式: 14 单:单一、一个 15 例:实例 16 一个构造函数一生只能有一个实例 =>不管你 new 多少次,都是这一个实例 17 应用: 18 自定义弹出层 19 核心代码: 20 let instance = null; 21 function singleTon(){ 22 if(!instance) instance = 实例对象; 23 return instance 24 } 25 问题: 26 1、书写代码的时候和构造函数已经没有关系 27 2、new 关键字没有了 28 */ 29 30 function Person(){ 31 this.name = 'Jack'; 32 } 33 34 let instance = null; 35 36 function singleTon(){ 37 if(!instance) instance = new Person(); 38 return instance 39 } 40 // 41 const p1 = singleTon(); 42 const p2 = singleTon(); 43 44 console.log(p1,p2); 45 console.log(p1===p2); 46 47 </script> 48 </html>
2、单例模式改进
1 <!DOCTYPE html> 2 <html lang="en"> 3 4 <head> 5 <meta charset="UTF-8"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>单例模式改造</title> 8 </head> 9 10 <body> 11 12 </body> 13 <script> 14 /* 15 单例模式改造 16 我需要把 instance 变量保存下来 17 singleTon 是一个函数,里面可以判断,可以返回 18 以闭包的形式来执行一段代码 19 为了保存构造函数,把他也写进闭包函数里面 20 如果你觉得需要使用 new ,那么你就直接写 21 */ 22 23 24 const Person = (function () { 25 // 真是构造函数体 26 function Person(name,age,gender) { 27 this.name = name; 28 this.age = age; 29 this.gender = gender; 30 } 31 32 Person.prototype.sayHi = function(){console.log('Hi')} 33 34 // 这个变量因为在一个不会被销毁的函数执行空间里面 35 // 所以会一直存在 36 let instance = null; 37 // 全局变量接收的是这个函数 38 return function singleTon(...arg) { 39 if (!instance) instance = new Person(...arg); 40 return instance 41 } 42 })() 43 44 const p1 = new Person('Jack',18,'男'); 45 const p2 = new Person('rose',20,'女'); 46 47 console.log(p1, p2); 48 console.log(p1 === p2); 49 </script> 50 51 </html>
3、观察者模式
1 <!DOCTYPE html> 2 <html lang="en"> 3 4 <head> 5 <meta charset="UTF-8"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>观察者模式</title> 8 </head> 9 10 <body> 11 12 </body> 13 <script> 14 /* 15 观察者模式 16 例子:监控 17 我们坐在教室里面就是 被观察者 18 监控后面的老师,就是 观察者 19 当被观察者触发了一些条件的时候,观察者就会触发技能 20 观察者模式:监控一个 对象 的状态,一旦状态发生改变,马上触发技能 21 需要两个构造函数来实现 22 1、创建被观察者 23 属性,自己的状态 24 队列,记录都有谁观察着自己,数组[] 25 方法,能设置自己的状态,当我需要改变的时候,要触发这个方法改变状态 26 方法,添加观察者 27 方法,删除观察者 28 2、创建观察者 29 需要一个身份证明 30 需要一个技能 31 */ 32 33 // 观察者构造函数 34 class Observer { 35 constructor(name, fn = () => { }) { 36 this.name = name; 37 this.fn = fn; 38 } 39 } 40 41 // 创建两个观察者 42 const bzr = new Observer('班主任', (state) => { console.log('因为:' + state + ',把你家长叫来') }) 43 const njzr = new Observer('年纪主任', (state) => { console.log('因为:' + state + ',问你你是那个班的?') }) 44 const xz = new Observer('校长', (state) => { console.log('因为:' + state + ',骂你的班主任') }) 45 46 console.log(bzr, xz) 47 48 // 被观察者的构造函数 49 class Subject { 50 constructor(state) { 51 this.state = state; 52 // 数组用来保存观察着我的人 53 this.observers = []; 54 } 55 56 // 设置自己的状态 57 setState(val) { 58 this.state = val; 59 // 就需要把 我的所有观察者的技能都触发 60 // 遍历 this.observers 一次触发技能 61 this.observers.forEach(item => { 62 // item 就是每一个观察者,就是一个一个的对象 63 // console.log(item) 64 // 告诉他我改变成了什么状态 65 item.fn(this.state) 66 }) 67 } 68 69 // 添加观察者 70 addObserver(obs) { 71 // 谁是观察者,就传递谁进来 72 // 判断一下,如果观察者已存在,就不添加了 73 // some() 74 // const res = this.observers.some(item => item === obs) 75 // find() 76 // const res = this.observers.find(item => item === obs) 77 // indexOf() 78 // const res = this.observers.indexOf(obs) 79 // filter() 80 // const res = this.observers.filter(item => item === obs) 81 this.observers = this.observers.filter(item => item !== obs) 82 // findIndex() 83 // const res = this.observers.findIndex(item => item === obs) 84 this.observers.push(obs); 85 } 86 87 // 删除观察者 88 delObserver(obs) { 89 // 把 obs 观察者删除 90 // 直接使用 filter 方法 91 this.observers.filter(item => item !== obs) 92 } 93 } 94 95 // 创建一个被观察者 96 const xiaoming = new Subject('学习'); 97 98 // 给 xiaoming 添加一个观察者 99 xiaoming.addObserver(njzr); 100 xiaoming.addObserver(bzr); 101 xiaoming.addObserver(xz); 102 103 console.log(xiaoming); 104 105 // 创建一个被观察者 106 const xiaohong = new Subject('学习'); 107 108 // 给 xiaoming 添加一个观察者 109 xiaoming.addObserver(bzr); 110 xiaoming.addObserver(xz); 111 112 113 114 115 116 </script> 117 118 </html>
4、发布订阅模式
1 <!DOCTYPE html> 2 <html lang="en"> 3 4 <head> 5 <meta charset="UTF-8"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>发布订阅模式</title> 8 </head> 9 10 <body> 11 12 </body> 13 <script> 14 /* 15 发布订阅模式: 16 有一个对象,有人一直看着他 17 当这个对象发生变化的时候,第三方通知这个看着的人,触发技能 18 例子:买书 19 1、普通程序员买书 20 去书店,问,没有,回家 21 过一会再去,问,没有,回家 22 过一会再去,问,没有,回家 23 2、发布订阅的程序员 24 去书店,问,没有,留下一个联系方式给店员 25 一旦有了书,就会接到电话 26 触发技能买书 27 只需要一个构造函数 28 创建一个第三方店员的身份 29 我们的任务就是模拟一个 addEventListener() 30 31 分析构造函数 32 属性:消息队列 33 { 34 click:[fn1, fn2], 35 abc:[fn1, fn2, fn3] 36 } 37 方法:能向消息队列里面添加内容 38 方法:删除消息队列里面的内容 39 方法:触发消息队列里面的内容 40 */ 41 42 // 创建一个第三方观察者构造函数 43 class Observer { 44 constructor() { 45 this.message = {} 46 } 47 // 1、向消息队列里面添加内容 48 on(type, fn) { 49 // type 我拜托你看着的行为 50 // fn 我要你在行为发生的时候做的事情 51 // 就应该把写着记录再消息队列 52 // 1、判断 message 里面有没有这个行为已经被注册过了 53 // 如果没有,我应该给你赋值一个这个行为,值赋值为[] 54 // 如果有,直接向数组里面添加就可以了。 55 if (!this.message[type]) { 56 // 表示消息队列里面还没有注册过 57 this.message[type] = [] 58 } 59 // 直接进行 push 60 this.message[type].push(fn) 61 } 62 // 2、删除消息队列里面的内容 63 off(type, fn) { 64 // 判断 如果 fn 不存在,只有一个参数的情况 65 if (!fn) { 66 // 直接把这个事情取消掉 67 // 从 message 里面直接把 abc 成员删除掉,就好了 68 delete this.message[type] 69 return 70 } 71 // 代码能来到这里,表示 fn 存在 72 // 判断你是不是真的订阅过 73 if (!this.message[type]) return 74 75 // 你确实订阅过,就可以使用 filter 删除了 76 this.message[type] = this.message[type].filter(item => item !== fn) 77 } 78 // 3、触发消息队列 79 trigger(type) { 80 // 判断是不是有订阅过 81 if (!this.message[type]) return; 82 83 // 找到这个数组,把里面的每一个函数都触发 84 this.message[type].forEach(item => { 85 // item 就是每一个函数 86 item(); 87 }) 88 } 89 } 90 91 // 使用构造函数创建一个实例 92 const person1 = new Observer(); 93 94 // 订阅 95 // 当你向拜托这个 person1 帮你观察一些内容的时候 96 // 告诉你一个行为,当行为出现的时候,告诉你干什么 97 person1.on('abc', handlerA); 98 person1.on('abc', handlerB); 99 person1.on('犀牛书', handlerC); 100 101 102 //取消订阅 103 // 告诉 person1 ,我这个事情不用你管了 104 // 1、我告诉你这个事情不用你管了 105 // person1.off('abc'); //把消息队列 里面属于 abc 的数组清空掉 106 // person1.off('a',handlerA) //告诉你 a 发生的时候,不用做 handlerA 这个事情了 107 108 // person1.off('c', handlerA); 109 // person1.off('abc', handlerA); 110 111 // 触发消息队列 112 // person1 这个人一旦触发 a 行为,就要把后面的所有事情处理函数执行掉 113 person1.trigger('abc'); 114 115 console.log(person1) 116 117 function handlerA() { console.log('handlerA') } 118 function handlerB() { console.log('handlerB') } 119 function handlerC() { console.log('handlerC') } 120 function handlerD() { console.log('handlerD') } 121 function handlerE() { console.log('handlerE') } 122 function handlerF() { console.log('handlerF') } 123 124 125 126 127 128 </script> 129 130 </html>
5、策略模式
1 <!DOCTYPE html> 2 <html lang="en"> 3 4 <head> 5 <meta charset="UTF-8"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>策略模式</title> 8 </head> 9 10 <body> 11 12 </body> 13 <script> 14 /* 15 策略模式: 16 + 一个问题匹配多个解决方案,不一定要用到那一个 17 + 而且有可能随时还会继续增加多个方案 18 例子:购物车结算 19 => 我们有好多种折扣计算方式 20 => 满 100 减 20 21 => 满 200 减 25 22 => 8 折 23 => 7 折 24 25 1、把我们的多种方案,以闭包的形式保存起来 26 + 按照传递进来的折扣和价格计算最终结果返回 27 2、留下添加和删除的折扣的接口 28 + 函数也是一个对象 29 + 可以把函数名当作一个对象,向里面添加一些成员 30 */ 31 32 // 简单实现:接收两个参数:price type 33 // 一旦加一种折扣,就很麻烦 34 // function calcPrice(price, type) { 35 // if (type === '100_10') { 36 // price -= 30 37 // } else if (type === '200_25') { 38 // price -= 25 39 // } else if (type === '80%') { 40 // price *= 0.8 41 // } else { 42 // console.log('没有这种折扣') 43 // } 44 // return price 45 // } 46 47 48 49 50 51 // 1、暂时以闭包的形式把折扣方案保存下来 52 // const calcPrice = (function () { 53 54 // const sale = { 55 // '100_10': price => price -= 10, 56 // '200_25': price => price -= 25, 57 // '80%': price => price *= 0.8, 58 // } 59 60 // // 被 return 出去的函数,才是 calcPrice 本体 61 // return function calcPrice(price, type) { 62 // // 判断对象里面有没有这个折扣类型 63 // // 如果有,那么就执行 64 // // 如果没有,那么就告诉他没有这个折扣类型 65 // if (!sale[type]) return '没有这个折扣'; 66 67 // // 找到 sale 里面指定的那个函数执行计算出结果,返回给外边 68 // return sale[type](price) 69 // } 70 // })() 71 72 73 // 2、留下添加和删除折扣的接口 74 const calcPrice = (function () { 75 const sale = { 76 '100_10': price => price -= 10, 77 '200_25': price => price -= 25, 78 '80%': price => price *= 0.8, 79 } 80 81 function calcPrice(price, type) { 82 if (!sale[type]) return '没有这个折扣'; 83 return sale[type](price) 84 } 85 86 // 把函数当作一个对象,向里面添加一些成员 87 calcPrice.add = function (type, fn) { 88 // 专门用来添加折扣 89 // 判断这个折扣是不是存在 90 if (sale[type]) return '该折扣已经存在'; 91 92 // 代码来到这里,表示折扣不存在 93 sale[type] = fn; 94 return '添加成功' 95 } 96 97 // 删除一个折扣 98 calcPrice.del = function (type) { 99 // 把对应的折扣删除掉 100 delete sale[type] 101 } 102 103 return calcPrice 104 })() 105 106 // 使用的时候 107 // 添加一个折扣 108 calcPrice.add('70%', function (price) { return price *= 0.7 }) 109 const res = calcPrice(320, '70%') 110 console.dir(calcPrice) 111 console.dir(res) 112 113 // 删除一个折扣 114 calcPrice.del('100_10'); 115 const res2 = calcPrice(320, '100_10'); 116 console.log(res2) 117 118 // const res = calcPrice(320, '200_25'); 119 // const res = calcPrice(320, '70%'); 120 // console.log(res) 121 122 123 124 125 126 127 </script> 128 129 </html>