19_设计模式

一、什么是设计模式

针对“特定问题”,给出的简洁而优化的处理方案

  • 一个设计模式A

    • 只能解决A类型问题

    • 针对B类型的问题,设计模式A解决不了

  • 同一个问题,在不同的位置,是不一定能用同一个方案解决的

  • 设计模式,只在特定的情况,特定的时期,针对特定的问题使用

  • 市面上的设计模式

    • 创建型模式:工厂方法模式,单例模式…

    • 结构型模式:组合模式,适配器模式…

    • 行为型模式:观察者模式,中介者模式…

二、单例模式

  • 什么是单例模式呢?

    • 单:单一,一个,唯一

    • 例:实例,构造函数的实例化对象

    • 让一个构造函数一辈子只有一个实例对象

      • 当你需要一个构造函数一生只能new出一个对象的时候
      • 就可以使用单例模式
  • 我们都知道,构造函数可以创造一个对象

  • 我们 new 很多次构造函数就能得到很多的对象

  • 单例模式: 就是使用构造函数实例化的时候,不管实例化多少回,都是同一个对象

    • 也就是一个构造函数一生只能 new 出一个对象
  • 也就是说,当我们使用构造函数,每一次 new 出来的对象 属性/功能/方法 完全一样 的时候,我们把他设计成单例模式

1.单例模式的简单应用

  • 弹出层alert()比较丑,用户体验极度不好

  • 好多网站会使用一个自己写的div盒子,当作弹出层

  • 在自己写的过程中,一个网站不可能只弹出一次

    • 不能每次弹出就创建一个div

    • 每次弹出的都是之前创建好的那个div,只是文字改变了

  • 创建div并显示出来的构造函数

    • 如果不用单例模式,每次new就是创建一个div
    • 如果用了单例模式,每次new都是用的第一次的div,只是文字改变

2.核心代码

  • 单例模式的核心代码很简单

  • 其实就是判断一下,他曾经有没有 new 出来过对象

  • 如果有,就还继续使用之前的那个对象,如果没有,那么就给你 new 一个

    // 准备一个构造函数
    // 将来要 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
    

3.应用

  • 我们就用这个核心代码简单书写一个 demo

    // 这个构造函数的功能就是创建一个 div,添加到页面中
    function CreateDiv() {
        this.div = document.createElement('div')
        document.body.appendChild(this.div)
    }
    
    CreateDiv.prototype.init = function (text) {
        this.div.innerHTML = text
    }
    
    // 准备把这个 CreateDiv 做成单例模式
    // 让 singleton 成为一个闭包函数
    const singleton = (function () {
    
        let instance
    
        return function (text) {
            if (!instance) {
                instance = new CreateDiv()
            }
            instance.init(text)
            return instance
        }
    })()
    
    singleton('hello') // 第一次的时候,页面中会出现一个新的 div ,内容是 hello
    singleton('world') // 第二次的时候,不会出现新的 div,而是原先的 div 内容变成了 world
    

三、观察者模式

  • 观察者模式,通常也被叫做 发布-订阅模式 或者 消息模式
  • 英文名称叫做 Observer
  • 官方解释: 当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新,解决了主体对象与观察者之间功能的耦合,即一个对象状态改变给其他对象通知的问题

1.例子

  • 当你想去书店买书,但是恰巧今天你要买的书没有了
  • 我们又不能总在书店等着,就把我们的手机留给店员
  • 当你需要的书到了的时候,他会打电话通知你,你去买了就好了
  • 你买到数了以后,就告诉他,我买到了,那么以后再来了书就不会通知你了

2.addEventListener

  • 上面的例子可能还不是很明确

  • 但是 addEventListener 是一个我们都用过的东西

  • 这个东西其实就是一个标准的 观察者模式

    btn.addEventListener('click', function () {
        console.log('btn 被点击了')
    })
    
    • 上面这个就是有一个 无形的观察者 再观察着 btn 的一举一动
    • 当这个 btn 被点击的时候,就会执行 对应的函数
    • 我们也可以多绑定几个函数
  • 说白了: 观察者模式就是我们自己实现一个 addEventListener 的功能

    • 只不过 addEventListaner 只有固定的一些事件,而且只能给 dom 元素绑定
    • 而我们自己写的可以随便绑定一个事件名称,自己选择触发时机而已

3.书写代码

  • 首先我们分析功能

    • 我们要有一个观察者(这里抽象为一个对象 {}

    • 需要有一个属性,存放消息的盒子(把你绑定的所有事件放在里面)

    • 需要一个 on 方法,用于添加事件

    • 需要一个 emit 方法,用于发布事件(触发)

    • 需要一个 off 方法,把已经添加的方法取消

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

      class Observer {
          constructor () {
              this.message = {}
          }
          
          on () {}
          
          emit () {}
          
          off () {}
      }
      
    • 现在,一个观察者的雏形就出来了

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

ON

  • 先来写 ON 方法

  • 添加一个事件

  • 我们的 on 方法需要接受 两个参数

    • 事件类型
    • 事件处理函数
    class Observer {
        constructor () {
            this.message = {}
        }
        
        on (type, fn) {
            // 判断消息盒子里面有没有设置事件类型
            if (!this.message[type]) {
                // 证明消息盒子里面没有这个事件类型
                // 那么我们直接添加进去
                // 并且让他的值是一个数组,再数组里面放上事件处理函数
                this.message[type] = [fn]
            } else {
                // 证明消息盒子里面有这个事件类型
                // 那么我们直接向数组里面追加事件处理函数就行了
                this.message[type].push(fn)
            }
        }
        
        emit () {}
        
        off () {}
    }
    

EMIT

  • 接下来就是发布事件

  • 也就是让我们已经订阅好的事件执行一下

  • 同样需要接受两个参数

    • 要触发的事件类型
    • 给事件处理函数传递的参数
    class Observer {
        constructor () {
            this.message = {}
        }
        
        on (type, fn) {
            // 判断消息盒子里面有没有设置事件类型
            if (!this.message[type]) {
                // 证明消息盒子里面没有这个事件类型
                // 那么我们直接添加进去
                // 并且让他的值是一个数组,再数组里面放上事件处理函数
                this.message[type] = [fn]
            } else {
                // 证明消息盒子里面有这个事件类型
                // 那么我们直接向数组里面追加事件处理函数就行了
                this.message[type].push(fn)
            }
        }
        
        emit (type, ...arg) {
            // 判断你之前有没有订阅过这个事件
            if (!this.message[type]) return
    
            // 如果有,那么我们就处理一下参数
            const event = {
                type: type,
                arg: arg || {}
            }
    
            // 循环执行为当前事件类型订阅的所有事件处理函数
            this.message[type].forEach(item => {
                item.call(this, event)
            })
        }
        
        off () {}
    }
    

OFF

  • 最后就是移除事件

  • 就是把已经订阅的事件处理函数移除掉

  • 同样需要接受两个参数

    • 要移除的事件类型
    • 要移除的事件处理函数
    class Observer {
        constructor () {
            this.message = {}
        }
        
        on (type, fn) {
            // 判断消息盒子里面有没有设置事件类型
            if (!this.message[type]) {
                // 证明消息盒子里面没有这个事件类型
                // 那么我们直接添加进去
                // 并且让他的值是一个数组,再数组里面放上事件处理函数
                this.message[type] = [fn]
            } else {
                // 证明消息盒子里面有这个事件类型
                // 那么我们直接向数组里面追加事件处理函数就行了
                this.message[type].push(fn)
            }
        }
        
        emit (type, ...arg) {
            // 判断你之前有没有订阅过这个事件
            if (!this.message[type]) return
    
            // 如果有,那么我们就处理一下参数
            const event = {
                type: type,
                arg: arg || {}
            }
    
            // 循环执行为当前事件类型订阅的所有事件处理函数
            this.message[type].forEach(item => {
                item.call(this, event)
            })
        }
        
        off (type, fn) {
            // 判断你之前有没有订阅过这个事件
            if (!this.message[type]) return
    
            // 如果有我们再进行移除
            for (let i = 0; i < this.message[type].length; i++) {
                const item =  this.message[type][i]
                if (item === fn) {
                    this.message[type].splice(i, 1)
                    i--
                }
            }
        }
    }
    
  • 以上就是最基本的 观察者模式

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

4.使用

const o = new Observer()

// 准备两个事件处理函数
function a(e) {
    console.log('hello')
}

function b(e) {
    console.log('world')
}

// 订阅事件
o.on('abc', a)
o.on('abc', b)

// 发布事件(触发)
o.emit('abc', '100', '200', '300') // 两个函数都回执行

// 移除事件
o.off('abc', 'b')

// 再次发布事件(触发)
o.emit('abc', '100', '200', '300') // 只执行一个 a 函数了

四、浏览器垃圾回收机制

浏览器的垃圾回收机制

  • 就是javascript执行环境会负责代码执行过程中使用的内存
  • 原理:垃圾收集器会定期(周期性)找出不再使用的变量,释放器内存
  • 垃圾回收时停止响应其他操作,垃圾回收开销比较大,所以这个过程不是实时的,而是按照固定的时间间隔周期性的执行
  • 两种实现方式
    • 标记清除 - 较为常用
    • 引用计数 - 不太常用
  • 大部分浏览器使用的时标记清除的垃圾回收策略或者类似的策略,只不过垃圾手机的时间间隔互不相同

什么时候触发垃圾回收机制

  • js引擎垃圾回收方案时标记清除
  • 就是遍历所有可访问的对象
  • 回收已不可访问的对象

五、Event Loop事件轮询机制

  • js是单线程的
  • 所有的js代码都是在主线程执行的
  • 不能同时执行多个任务,每次要上一句代码执行完了才能执行下一句代码
  • 同步的代码执行
  • 异步的代码分配到对应的管理模块
    • 定时器管理模块
    • dom事件管理模块
    • 渲染模块- 和js主线程是互斥的
  • 当管理模块监控对应的任务,如果条件符合,就把要执行的代码放到回调队列中
  • 回调队列有微任务和宏任务
  • 同步代码执行完以后,主线程就去回调队列询问,是否有任务要执行
  • 如果回调队列中没有任务要执行,就进行下一次询问―(轮询)
  • 如果回调队列中有任务要执行,先执行微任务再执行宏任务
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值