⚡️前端设计模式与应用 | 青训营笔记

62 阅读4分钟

这是我参与「第四届青训营 」笔记创作活动的第2天

1、常见问题的解决方案模型

  • 根据历史经验的总结
  • 与特定语言无关

2、浏览器的设计模式

1. 单例

定义

全局唯一访问的对象,保证一个类在全局当中,只有一个实例对象,这种模式叫做单例

应用场景

缓存,全局状态管理等(例如咱们的 Vuex)

单例的实现方式

首先,课上给出「用单例模式实现请求缓存」如下图所示 然后我给出我对「单例模式」的实现步骤

  1. 我们得有一个类( Single ,它的功能就是创建实例对象,但是得在内部函数当中判断是否创建过对象
  2. 调用Single的 static 方法创建实例,进入方法当中时对类是否创建过实例做个判断,第一次进入的方法才返回new Single,之后的都返回这个已经创建好的实例
  3. 所以咱们的实例得到的都是一个实例

话不多说,代码模拟一波~

class Single {
  static getInstance() {
    // 判断是否第一次创建实例
    if (!Single.instance) {
      Single.instance = new Single();
    }
      return Single.instance;
  }
}

let sin1 = Single.getInstance();
let sin2 = Single.getInstance();

console.log(sin1 == sin2) // true

以上就是我们简单的单例模拟啦~其中的核心思想还是实现全局唯一访问的对象

2. 发布与订阅

定义

一种订阅机制,可以在被订阅对象发生变化时通知订阅者

应用场景

统架构之间的解耦,到业务中的实现模式,例如邮件订阅,上线订阅等

发布与订阅的实现方式

首先,课上给出「用发布与订阅模式实现用户上线订阅」如下图所示

其次,我对发布与订阅理解如下~

  1. 发布订阅的意义就是在于,使用一个中介(eventHub) 来事件的操控进行存储操作,类似于A泡妞的方法存储起来(on),B看到这本子就可以使用(emit),为了不给人看见,必要时候可以抹出(off)
  2. 所以我们得实现三个方法,on是对同名的事件的存储,emit是触发同名事件的所有函数,off将函数出该名函数群中去除

实现步骤为:

  • 首先得对事件有个存储,因为事件是按照添加的执行顺序执行,所以得使用队列这数据结构,同个名的函数,添加进queueMap任务队列当中,按照订阅的创建顺序所定,呈映射关系
  • 在off当中,是在任务队列中找到fn是否存在,返回其索引值(index),再将其删除
  • queeMap是在eventHub上的,调用时得加前缀

实现代码如下:

// 中间(存储发布与订阅)
const eventHub = {
    // 存储任务队列的集合
    queueMap: {},
    // 订阅
    on: (name, fn) => {
        // 考虑name队列不存在的情况
        // if (queueMap[name] !== undefined) {
        //     queueMap[name] = [];
        // }
        eventHub.queueMap[name] = eventHub.queueMap[name] || []; // 代替上面的,使用设计模式:防御式编程
        eventHub.queueMap[name].push(fn); // 将事件名为name的队列,添加回调函数
        // return undefined;
    },
    // 发布
    emit: (name, data) => {
        const q = eventHub.queueMap[name]; // alins缩写设计模式
        // 先判断为name的队列是否存在
        if (!q) { return } // 设计模式:短路(不存在直接return,省一个else)
        q.map(fn => fn(data)); // 对其任务队列的任务,进行执行
        // return undefined;
    },
    // 取消订阅(找到任务队列的指定任务,再把它ko掉)
    off: (name, fn) => {
        const q = eventHub.queueMap[name]; // alins缩写设计模式
        if (!q) { return };
        // 找到任务队列当中是否有fn
        const index = q.indexOf(fn);
        if (index < 0) { return }; // 没找到呀,直接返回
        q.splice(index, 1); // 删除为fn的任务
        return undefined;
    },
}

eventHub.on('click', console.log); // 参数:方法名,回调函数

// 一段时间,发布函数
setTimeout(() => {
    eventHub.emit('click', 'fuk'); // 参数:指定的方法,输入的值
}, 2000);

3、javascript 设计模式

1. 原型模式

定义

复制已有模式来创建新的对象,我们使用原型模式,是为了得到与构造函数(类)相对应的类型的实例、实现数据/方法的共享,并不像有些资料说的,目的是为了克隆

应用场景:

JS中对象创建的基本模式

2. 代理模式

定义 

可自定义控制叹对原对象的访问方式,并且允许在更新前后做一些额外处理

应用场景

监控(代理 fetch ),代理工具,前端框架实现等等

3. 迭代器模式

定义

在不暴露数据类型的情况下访问集合中的数据

应用场景

数据结构中有多种数据类型,列表,树等,提供通用操作接口

4、前端框架中的设计模式

1. 代理模式

目的是实现它

2. 组合模式

定义

多个对象组合使用,也可以单个对象独立使用

应用场景

DOM,前端组件,文件目录,部门