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>


posted @ 2020-10-31 14:37  小笑残虹  阅读(173)  评论(0编辑  收藏  举报