设计模式

  • 设计模式是我们在 解决问题的时候针对特定问题给出的简洁而优化的处理方案
  • 我们有很多的设计模式
    • 单例模式
    • 组合模式
    • 观察者模式
  • 今天我们就聊一下这三个设计模式
    • 单例模式 / 组合模式 / 观察者模式

单例模式

  • 什么是单例模式呢?
  • 我们都知道,构造函数可以创造一个对象
  • 我们 new 很多次构造函数就能得到很多的对象
  • 单例模式: 就是使用构造函数实例化的时候,不管实例化多少回,都是同一个对象
    • 也就是一个构造函数一生只能 new 出一个对象
  • 也就是说,当我们使用构造函数,每一次 new 出来的对象 属性/功能/方法 完全一样 的时候,我们把他设计成单例模式

核心代码

  • 单例模式的核心代码很简单
  • 其实就是判断一下,他曾经有没有 new 出来过对象
  • 如果有,就还继续使用之前的那个对象,如果没有,那么就给你 new 一个 ```javascript // 准备一个构造函数 // 将来要 new 的 function Person() {}

// 准备一个单例模式函数 // 这个单例模式函数要把 Person 做成一个单例模式 // 将来再想要 new Person 的时候只要执行这个 singleton 函数就可以了 function singleton () { let instance

if (!instance) { // 如果 instance 没有内容 // 来到这里,证明 instance 没有内容 // 给他赋值为 new Person instance = new Person() }

// 返回的永远都是第一次 new Person 的实例 // 也就是永远都是一个实例 return instance }

const p1 = singleton() const p2 = singleton() console.log(p1 === p2) // true

  1. <a name="5b0520a9"></a>
  2. ### 应用
  3. - 我们就用这个核心代码简单书写一个 demo
  4. ```javascript
  5. // 这个构造函数的功能就是创建一个 div,添加到页面中
  6. function CreateDiv() {
  7. this.div = document.createElement('div')
  8. document.body.appendChild(this.div)
  9. }
  10. CreateDiv.prototype.init = function (text) {
  11. this.div.innerHTML = text
  12. }
  13. // 准备把这个 CreateDiv 做成单例模式
  14. // 让 singleton 成为一个闭包函数
  15. const singleton = (function () {
  16. let instance
  17. return function (text) {
  18. if (!instance) {
  19. instance = new CreateDiv()
  20. }
  21. instance.init(text)
  22. return instance
  23. }
  24. })()
  25. singleton('hello') // 第一次的时候,页面中会出现一个新的 div ,内容是 hello
  26. singleton('world') // 第二次的时候,不会出现新的 div,而是原先的 div 内容变成了 world

组合模式

  • 组合模式,就是把几个构造函数的启动方式组合再一起
  • 然后用一个 ”遥控器“ 进行统一调用 ```javascript class GetHome {

    init () {

    1. console.log('到家了')

    } }

class OpenComputer {

  1. init () {
  2. console.log('打开电脑')
  3. }

}

class PlayGame {

  1. init () {
  2. console.log('玩游戏')
  3. }

}

  1. - 上面几个构造函数的创造的实例化对象的 **启动方式** 都一致
  2. - 那么我们就可以把这几个函数以组合模式的情况书写
  3. - 然后统一启动
  4. - 准备一个 **组合模式** 的构造函数
  5. ```javascript
  6. class Compose {
  7. constructor () {
  8. this.compose = []
  9. }
  10. // 添加任务的方法
  11. add (task) {
  12. this.compose.push(task)
  13. }
  14. // 一个执行任务的方法
  15. execute () {
  16. this.compose.forEach(item => {
  17. item.init()
  18. })
  19. }
  20. }
  • 我们就用我们的组合模式构造函数来吧前面的几个功能组合起来 ```javascript const c = new Compose() // 把所有要完成的任务都放在队列里面 c.add(new GetHome()) c.add(new OpenComputer) c.add(new PlayGame)

// 直接器动任务队列 c.execute() // 就会按照顺序执行三个对象中的 init 函数

  1. <a name="ed69b1eb"></a>
  2. ## 观察者模式
  3. - 观察者模式,通常也被叫做 **发布-订阅模式** 或者 **消息模式**
  4. - 英文名称叫做 `**Observer**`
  5. - 官方解释: 当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新,解决了主体对象与观察者之间功能的耦合,即一个对象状态改变给其他对象通知的问题
  6. - 听起来很迷糊,但是其实没有很难
  7. <a name="0796535a"></a>
  8. ### 一个例子
  9. - 当你想去书店买书,但是恰巧今天你要买的书没有了
  10. - 我们又不能总在书店等着,就把我们的手机留给店员
  11. - 当你需要的书到了的时候,他会打电话通知你,你去买了就好了
  12. - 你买到数了以后,就告诉他,我买到了,那么以后再来了书就不会通知你了
  13. <a name="addEventListener"></a>
  14. ### addEventListener
  15. - 上面的例子可能还不是很明确
  16. - 但是 `addEventListener` 是一个我们都用过的东西
  17. - 这个东西其实就是一个标准的 **观察者模式**
  18. ```javascript
  19. btn.addEventListener('click', function () {
  20. console.log('btn 被点击了')
  21. })
  • 上面这个就是有一个 无形的观察者 再观察着 btn 的一举一动
  • 当这个 btn 被点击的时候,就会执行 对应的函数
  • 我们也可以多绑定几个函数
    • 说白了: 观察者模式就是我们自己实现一个 addEventListener 的功能
  • 只不过 addEventListaner 只有固定的一些事件,而且只能给 dom 元素绑定
  • 而我们自己写的可以随便绑定一个事件名称,自己选择触发时机而已

书写代码

  • 首先我们分析功能

    • 我们要有一个观察者(这里抽象为一个对象 {}
    • 需要有一个属性,存放消息的盒子(把你绑定的所有事件放在里面)
    • 需要一个 on 方法,用于添加事件
    • 需要一个 emit 方法,用于发布事件(触发)
    • 需要一个 off 方法,把已经添加的方法取消

      1. const observer = {
      2. message: {},
      3. on: function () {},
      4. emit: function () {},
      5. off: function () {}
      6. }
    • 我们把它写成一个构造函数的形式

      1. class Observer {
      2. constructor () {
      3. this.message = {}
      4. }
      5. on () {}
      6. emit () {}
      7. off () {}
      8. }
    • 现在,一个观察者的雏形就出来了

    • 接下来完善方法就可以了

ON

  • 先来写 ON 方法
  • 添加一个事件
  • 我们的 on 方法需要接受 两个参数

    • 事件类型
    • 事件处理函数

      1. class Observer {
      2. constructor () {
      3. this.message = {}
      4. }
      5. on (type, fn) {
      6. // 判断消息盒子里面有没有设置事件类型
      7. if (!this.message[type]) {
      8. // 证明消息盒子里面没有这个事件类型
      9. // 那么我们直接添加进去
      10. // 并且让他的值是一个数组,再数组里面放上事件处理函数
      11. this.message[type] = [fn]
      12. } else {
      13. // 证明消息盒子里面有这个事件类型
      14. // 那么我们直接向数组里面追加事件处理函数就行了
      15. this.message[type].push(fn)
      16. }
      17. }
      18. emit () {}
      19. off () {}
      20. }

EMIT

  • 接下来就是发布事件
  • 也就是让我们已经订阅好的事件执行一下
  • 同样需要接受两个参数

    • 要触发的事件类型
    • 给事件处理函数传递的参数

      1. class Observer {
      2. constructor () {
      3. this.message = {}
      4. }
      5. on (type, fn) {
      6. // 判断消息盒子里面有没有设置事件类型
      7. if (!this.message[type]) {
      8. // 证明消息盒子里面没有这个事件类型
      9. // 那么我们直接添加进去
      10. // 并且让他的值是一个数组,再数组里面放上事件处理函数
      11. this.message[type] = [fn]
      12. } else {
      13. // 证明消息盒子里面有这个事件类型
      14. // 那么我们直接向数组里面追加事件处理函数就行了
      15. this.message[type].push(fn)
      16. }
      17. }
      18. emit (type, ...arg) {
      19. // 判断你之前有没有订阅过这个事件
      20. if (!this.message[type]) return
      21. // 如果有,那么我们就处理一下参数
      22. const event = {
      23. type: type,
      24. arg: arg || {}
      25. }
      26. // 循环执行为当前事件类型订阅的所有事件处理函数
      27. this.message[type].forEach(item => {
      28. item.call(this, event)
      29. })
      30. }
      31. off () {}
      32. }

OFF

  • 最后就是移除事件
  • 就是把已经订阅的事件处理函数移除掉
  • 同样需要接受两个参数

    • 要移除的事件类型
    • 要移除的事件处理函数

      1. class Observer {
      2. constructor () {
      3. this.message = {}
      4. }
      5. on (type, fn) {
      6. // 判断消息盒子里面有没有设置事件类型
      7. if (!this.message[type]) {
      8. // 证明消息盒子里面没有这个事件类型
      9. // 那么我们直接添加进去
      10. // 并且让他的值是一个数组,再数组里面放上事件处理函数
      11. this.message[type] = [fn]
      12. } else {
      13. // 证明消息盒子里面有这个事件类型
      14. // 那么我们直接向数组里面追加事件处理函数就行了
      15. this.message[type].push(fn)
      16. }
      17. }
      18. emit (type, ...arg) {
      19. // 判断你之前有没有订阅过这个事件
      20. if (!this.message[type]) return
      21. // 如果有,那么我们就处理一下参数
      22. const event = {
      23. type: type,
      24. arg: arg || {}
      25. }
      26. // 循环执行为当前事件类型订阅的所有事件处理函数
      27. this.message[type].forEach(item => {
      28. item.call(this, event)
      29. })
      30. }
      31. off (type, fn) {
      32. // 判断你之前有没有订阅过这个事件
      33. if (!this.message[type]) return
      34. // 如果有我们再进行移除
      35. for (let i = 0; i < this.message[type].length; i++) {
      36. const item = this.message[type][i]
      37. if (item === fn) {
      38. this.message[type].splice(i, 1)
      39. i--
      40. }
      41. }
      42. }
      43. }
  • 以上就是最基本的 观察者模式

  • 接下来我们就使用一下试试看

使用一下

  1. const o = new Observer()
  2. // 准备两个事件处理函数
  3. function a(e) {
  4. console.log('hello')
  5. }
  6. function b(e) {
  7. console.log('world')
  8. }
  9. // 订阅事件
  10. o.on('abc', a)
  11. o.on('abc', b)
  12. // 发布事件(触发)
  13. o.emit('abc', '100', '200', '300') // 两个函数都回执行
  14. // 移除事件
  15. o.off('abc', 'b')
  16. // 再次发布事件(触发)
  17. o.emit('abc', '100', '200', '300') // 只执行一个 a 函数了