第125题 前端常用的设计模式和使用场景
- 工厂模式
- 用一个工厂函数来创建实例,使用的时候隐藏
new,可在工厂函数中使用new(function factory(a,b,c) {return new Foo()}) - 如
jQuery的$函数:$等于是在内部使用了new JQuery实例(用工厂函数$包裹了一下),可以直接使用$(div) react的createElement
- 用一个工厂函数来创建实例,使用的时候隐藏
- 单例模式
- 全局唯一的实例(无法生成第二个)
- 如
VuexRedux的store - 如全局唯一的
dialog、modal - 演示
// 通过class实现单例构造器
class Singleton {
private static instance
private contructor() {}
public static getInstance() {
if(!this.instance) {
this.instance = new Singleton()
}
return this.instance
},
fn1() {}
fn2() {}
}
// 通过闭包实现单例构造器
const Singleton = (function () {
// 隐藏Class的构造函数,避免多次实例化
function FooService() {}
// 未初始化的单例对象
let fooService;
return {
// 创建/获取单例对象的函数
// 通过暴露一个 getInstance() 方法来创建/获取唯一实例
getInstance: function () {
if (!fooService) {
fooService = new FooService();
}
return fooService;
}
}
})();
// 使用
const s1 = Singleton.getInstance()
const s2 = Singleton.getInstance()
// s1 === s2 // 都是同一个实例
- 代理模式
- 使用者不能直接访问对象,而是访问一个代理层
- 在代理层可以监听
getset做很多事 - 如
ES6 Proxy实现Vue3响应式
var obj = new Proxy({},{
get:function(target,key,receiver) {
return Refect.get(target,key,receiver)
},
set:function(target,key,value,receiver) {
return Refect.set(target,key,value,receiver)
}
})
- 观察者模式
- 观察者模式(基于发布订阅模式)有观察者,也有被观察者
- 观察者需要放到被观察者中,被观察者的状态变化需要通知观察者 我变化了,内部也是基于发布订阅模式,收集观察者,状态变化后要主动通知观察者
class Subject { // 被观察者 学生
constructor(name) {
this.state = 'happy'
this.observers = []; // 存储所有的观察者
}
// 收集所有的观察者
attach(o){ // Subject. prototype. attch
this.observers.push(o)
}
// 更新被观察者 状态的方法
setState(newState) {
this.state = newState; // 更新状态
// this 指被观察者 学生
this.observers.forEach(o => o.update(this)) // 通知观察者 更新它们的状态
}
}
class Observer{ // 观察者 父母和老师
constructor(name) {
this.name = name
}
update(student) {
console.log('当前' + this.name + '被通知了', '当前学生的状态是' + student.state)
}
}
let student = new Subject('学生');
let parent = new Observer('父母');
let teacher = new Observer('老师');
// 被观察者存储观察者的前提,需要先接纳观察者
student.attach(parent);
student.attach(teacher);
student.setState('被欺负了');
- 发布订阅模式
- 发布订阅者模式,一种对象间一对多的依赖关系,但一个对象的状态发生改变时,所依赖它的对象都将得到状态改变的通知。
- 主要的作用(优点):
- 广泛应用于异步编程中(替代了传递回调函数)
- 对象之间松散耦合的编写代码
- 缺点:
- 创建订阅者本身要消耗一定的时间和内存
- 多个发布者和订阅者嵌套一起的时候,程序难以跟踪维护
- 发布订阅者模式和观察者模式的区别?
- 发布/订阅模式是观察者模式的一种变形,两者区别在于,发布/订阅模式在观察者模式的基础上,在目标和观察者之间增加一个调度中心。
- 观察者模式 是由具体目标调度,比如当事件触发,
Subject就会去调用观察者的方法,所以观察者模式的订阅者与发布者之间是存在依赖的(互相认识的)。 - 发布/订阅模式 由统一调度中心调用,因此发布者和订阅者不需要知道对方的存在(
publisher和subscriber是不认识的,中间有个Event Channel隔起来了) - 总结一下:
- 观察者模式:
Subject和Observer直接绑定,没有中间媒介。如addEventListener直接绑定事件 - 发布订阅模式:
publisher和subscriber互相不认识,需要有中间媒介Event Channel。如EventBus自定义事件
- 观察者模式:
- 实现的思路:
- 创建一个对象(缓存列表)
on方法用来把回调函数fn都加到缓存列表中emit根据key值去执行对应缓存列表中的函数off方法可以根据key值取消订阅
class EventEmiter {
constructor() {
// 事件对象,存放订阅的名字和事件
this._events = {}
}
// 订阅事件的方法
on(eventName,callback) {
if(!this._events) {
this._events = {}
}
// 合并之前订阅的cb
this._events[eventName] = [...(this._events[eventName] || []),callback]
}
// 触发事件的方法
emit(eventName, ...args) {
if(!this._events[eventName]) {
return
}
// 遍历执行所有订阅的事件
this._events[eventName].forEach(fn=>fn(...args))
}
off(eventName,cb) {
if(!this._events[eventName]) {
return
}
// 删除订阅的事件
this._events[eventName] = this._events[eventName].filter(fn=>fn != cb && fn.l != cb)
}
// 绑定一次 触发后将绑定的移除掉 再次触发掉
once(eventName,callback) {
const one = (...args)=>{
// 等callback执行完毕在删除
callback(args)
this.off(eventName,one)
}
one.l = callback // 自定义属性
this.on(eventName,one)
}
}
// 测试用例
let event = new EventEmiter()
let login1 = function(...args) {
console.log('login success1', args)
}
let login2 = function(...args) {
console.log('login success2', args)
}
// event.on('login',login1)
event.once('login',login2)
event.off('login',login1) // 解除订阅
event.emit('login', 1,2,3,4,5)
event.emit('login', 6,7,8,9)
event.emit('login', 10,11,12)
- 装饰器模式
- 原功能不变,增加一些新功能(
AOP面向切面编程) ES和TS的Decorator语法就是装饰器模式
- 原功能不变,增加一些新功能(
经典设计模式有
23个,这是基于后端写的,前端不是都常用
