前端开发常见的几种设计模式:
- 观察者模式
- 描述:一个对象(称为主体)维护其依赖项列表(观察者列表),当对象状态发生变化时,它会通知所有观察者。
- 应用场景:事件监听、数据绑定等。
- 单例模式
- 描述:确保类只有一个实例,并提供全局访问点。
- 应用场景:全局状态管理、日志记录器等。
- 工厂模式
- 描述:定义一个创建对象的接口,但允许子类决定要实例化的类。
- 应用场景:组件或对象的创建。
这次我们主要聊聊非常常用的发布订阅。
发布 - 订阅模式又被称为观察者模式,它是定义在对象之间一对多的关系中,当一个对象发生变化,其他依赖于它的对象收到通知。在 javascript 的开发中,我们一般用事件模型替代发布 - 订阅模式。
举个栗子 **:**
假如一家在线商城需要实时更新商品的价格和库存数量,就可以使用发布订阅模式。如果消费者对这件商品进行关注(监听),当商品的价格 / 数量变化时,就可以推送给所有关注了该商品的用户。
# Event () 构造函数
# 语法
<span class="ne-text"> event = new Event(typeArg, eventInit);</span>
# 参数
是 DOMString 类型,表示所创建事件的名称。
eventInit 可选
是 EventInit 类型的字典,接受以下字段:
- "bubbles",可选,Boolean 类型,默认值为 false,表示该事件是否冒泡。
- "cancelable",可选,Boolean 类型,默认值为 false,表示该事件能否被取消。
- "composed",可选,Boolean 类型,默认值为 false,指示事件是否会在影子 DOM 根节点之外触发侦听器。
# 手写一个发布 - 订阅
class EventEmitter { | |
constructor() { | |
this.event = {} // 'run': [fun] | |
} | |
on(type, cb) { | |
if (!this.event[type]) { | |
this.event[type] = [cb] | |
} else { | |
this.event[type].push(cb) | |
} | |
} | |
once(type, cb) { | |
const fn = (...args) => { | |
cb(...args) | |
this.off(type, fn) | |
} | |
this.on(type, fn) | |
} | |
emit(type, ...args) { | |
if (!this.event[type]) { | |
return | |
} else { | |
this.event[type].forEach(cb => { | |
cb(...args) | |
}); | |
} | |
} | |
off(type, cb) { | |
if (!this.event[type]) { | |
return | |
} else { | |
this.event[type] = this.event[type].filter(item => item !== cb); | |
} | |
} | |
} |
- constructor():初始化了一个空对象 this.event,用于存储事件类型和相应的回调函数数组。
- on (type, cb):订阅事件的方法,用于添加事件监听器,接受两个参数,type 表示事件类型,cb 表示要执行的回调函数。如果该事件类型已经存在对应的回调函数数组,则将回调函数追加到数组中,否则创建一个新的数组并将回调函数存入。
- once (type, cb):执行单次事件订阅的方法,添加一次性事件监听器。内部定义了一个新的函数 fn,它会调用给定的回调函数 cb,然后立即移除这个监听器。最后,调用 on () 方法添加这个新定义的函数。
- emit (type, ...args):触发事件的方法,根据传入事件名称、参数遍历事件队列触发事件。如果存在对应的事件类型,就依次执行该类型下的所有回调函数,并将参数传递给它们。
- off (type, cb):移除指定事件类型下的指定回调函数。如果存在对应的事件类型,则从事件类型对应的回调函数数组中过滤掉要移除的回调函数。
# 如何使用发布 - 订阅模式来解决一个异步
<script> | |
let finish = new Event('finish') | |
function fnA() { | |
setTimeout(() => { | |
console.log('请求1完成'); | |
window.dispatchEvent(finish); | |
}, 1000) | |
} | |
function fnB() { | |
setTimeout(() => { | |
console.log('请求2完成'); | |
}, 500) | |
} | |
fnA() | |
window.addEventListener('finish', () => { | |
fnB() | |
}) | |
</script> |
# 发布 - 订阅的优缺点:
优点:
- 解耦性 :发布订阅模式可以有效地解耦发布者和订阅者之间的关系。发布者和订阅者之间不需要直接引用彼此,它们通过调度中心进行通信,从而降低了对象之间的依赖性。(PS: 在观察者模式中,被观察者和观察者之间的通信是直接的,即被观察者会直接调用观察者的方法进行通信。这种直接的通信方式可能会导致被观察者与观察者之间的耦合度较高。)
- 灵活性 :发布订阅模式提供了一种灵活的机制,使得可以轻松地添加新的发布者和订阅者,或者移除现有的发布者和订阅者,而不会影响到其他部分的代码。
- 异步通信 :发布 - 订阅模式支持异步通信,发布者和订阅者可以在不同的时间、不同的线程或者不同的进程中进行通信。
缺点:
- 增加消耗 :创建结构和缓存订阅者这两个过程需要消耗计算和内存资源,即使订阅后始终没有触发,订阅者也会始终存在于内存。
- 增加复杂度 :订阅者被缓存在一起,如果多个订阅者和发布者层层嵌套,那么程序将变得难以追踪和调试。