这是我参与「第四届青训营」笔记创作活动的的第5天
浏览器中设计模式
传统设计模式:单例模式
- 定义:全局唯一访问对象,如window对象。
- 应用场景:实现缓存、全局状态管理等。
- 比如以下请求缓存例子:cache作为全局对象,任何地方都可以使用保存响应结果;request为缓存方法。
- 第一次调用时,cache中还未存储响应结果,需要先发送请求获取响应结果,因此等待的时间会较长;第二次由于保存了发送请求后得到的响应结果,故等待时间较短。
const cache={} export const request = async(url:string)=>{ if(cache[url]) return cache[url] const response = await api(url) cache[url] = response return response }
发布订阅模式
- 定义:在被订阅对象发生变化时通知订阅者。
- 应用场景:邮件订阅、上线订阅等。通俗地讲,比如在Bilibili、Youtube中,你关注的博主发布了动态和视频,自己会接收到他们的发布通知。
- 以下为用户上线订阅例子。Notify为消息通知函数。包含订阅和上线发布方法。
- 默认构建用户是不在线的。关注用户时,会将用户信息与通知消息插入follower列表中;当用户上线时,会给自己的follower发送上线通知。
type Notify = (user=User) =>void //通知函数 export class User { constructor(name){ ... } //包含follower列表 subscribe(user = User,notify = Notify){ User.folowers.push({user,notify}) //新增订阅者 } online(){ ... //状态修改操作 this.followers.forEach(({notify})=>{ notify(this) //上线时通知follower }) } }
JavaScript设计模式
原型模式
- 定义:用已有对象创建新对象。
- 例子:使用原型模式创建上线订阅中的用户。
- JavaScript中Object提供有创建对象方法
Object.create(),可以在源对象的基础上创建新的对象,新对象可以使用源对象提供的属性和方法。export const createUser = (name) =>{ const user = Object.create(User) ... //属性赋值操作 return user } - 目前很少使用该方法了。
代理模式
- 定义:可自定义控制原型对象的访问方式,并允许在更新后做一些额外处理。
- 应用场景:监控、代理工具、前端框架实现等。
- 所谓代理,就是有一个替身代理对象去控制源对象的,避免对源对象直接操作。在替身代理对象中对请求进行处理后再转交给源对象。
- 如下例子:使用代理模式实现用户在线状态订阅。
- 在代理对象中,如要改变用户的上线状态时,会触发通知函数(通知followers用户上线了)。
const proxyUser = new Proxy(user,{ //代理用户 set(target,prop,value){ target[prop] = value if(prop === 'status'){ notifyStatusHandler(target,value) //当修改user中的状态时触发通知状态函数 } return true } }) - 一些其他代理模式案例:
- 图片预加载:当上传的图片过大时,可以用一张本地的图片先进行占位,等图片异步加载完成后再进行替换。
- 缓存代理:存储先前发送的请求结果,在下次请求时如果传入的参数一致,则返回先前的存储结果。
迭代器模式
- 定义:在不暴露数据类型的情况下访问集合中的数据。
- 应用场景:数据结构中多种数据类型:列表、树等,可以提供通用操作接口。
- 比如用
for...of迭代组件。
前端框架设计模式
代理模式
- 对DOM操作代理,比如前端框架中常用的MVVM模型。
- 拿Vue作为例子,Vue实例即数据代理。M(数据模型)-V(视图模型)-VM(Vue实例)模型中,通过VM对象代理data对象中属性,可以更方便操纵data中的数据。
- Vue实例数据代理原理:通过Object.defineProperty()把数据模型对象中所有属性都加到VM上,每个被VM代理的属性都有
get()和set()方法去获取、改变属性值。
组合模式
- 定义:多个对象组合使用,也可以单个对象独立使用。
- 应用场景:DOM、前端组件、文件目录、部门等。