定义
发布-订阅模式其实是一种对象间一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都将得到状态改变的通知。 订阅者(Subscriber)把自己想订阅的事件注册(Subscribe)到调度中心(Event Channel),当发布者(Publisher)发布该事件(Publish Event)到调度中心,也就是该事件触发时,由调度中心统一调度(Fire Event)订阅者注册到调度中心的处理代码。
实现思路
- 创建一个
EventEmitter
类 - 在该类上创建一个事件中心(Map)
on
方法用来把函数 fn 都加到事件中心中(订阅者注册事件到调度中心)emit
方法取到 arguments 里第一个当做 event,根据 event 值去执行对应事件中心中的函数(发布者发布事件到调度中心,调度中心处理代码)off
方法可以根据 event 值取消订阅(取消订阅)once
方法只监听一次,调用完毕后删除缓存函数(订阅一次)- 注册一个
newListener
用于监听新的事件订阅
第一步,创建一个类,并初始化一个事件存储中心
class EventEmitter{
// 用来存放注册的事件与回调
constructor(){
this._events = {};
}
}
第二步,实现事件的订阅方法 on
class EventEmitter{
// 用来存放注册的事件与回调
constructor(){
this._events = {};
}
on(eventName, callback){
// 由于一个事件可能注册多个回调函数,所以使用数组来存储事件队列
const callbacks = this._events[eventName] || [];
callbacks.push(callback);
this._events[eventName] = callbacks
}
}
第三步,实现事件的发布方法 emit
class EventEmitter{
// 用来存放注册的事件与回调
constructor(){
this._events = {};
}
// args 用于收集发布事件时传递的参数
emit(eventName, ...args){
const callbacks = this._events[eventName] || [];
callbacks.forEach(cb => cb(...args))
}
}
第四步,实现事件的取消订阅方法 off
class EventEmitter{
// 用来存放注册的事件与回调
constructor(){
this._events = {};
}
off(eventName, callback){
const callbacks = this._events[eventName] || []
const newCallbacks = callbacks.filter(fn => fn != callback && fn.initialCallback != callback /* 用于once的取消订阅 */)
this._events[eventName] = newCallbacks;
}
}
第五步,实现事件的单次订阅方法 once
class EventEmitter{
// 用来存放注册的事件与回调
constructor(){
this._events = {};
}
//
once(eventName, callback){
// 由于需要在回调函数执行后,取消订阅当前事件,所以需要对传入的回调函数做一层包装,然后绑定包装后的函数
const one = (...args)=>{
// 执行回调函数
callback(...args)
// 取消订阅当前事件
this.off(eventName, one)
}
// 考虑:如果当前事件在未执行,被用户取消订阅,能否取消?
// 由于:我们订阅事件的时候,修改了原回调函数的引用,所以,用户触发 off 的时候不能找到对应的回调函数
// 所以,我们需要在当前函数与用户传入的回调函数做一个绑定,我们通过自定义属性来实现
one.initialCallback = callback;
this.on(eventName, one)
}
}
第六步,注册一个 newListener
用于监听新的事件订阅
class EventEmitter{
// 用来存放注册的事件与回调
constructor(){
this._events = {};
}
on(eventName, callback){
// 如果绑定的事件不是newListener 就触发改回调
if(this._events[eventName]){
if(this.eventName !== "newListener"){
this.emit("newListener", eventName)
}
}
// 由于一个事件可能注册多个回调函数,所以使用数组来存储事件队列
const callbacks = this._events[eventName] || [];
callbacks.push(callback);
this._events[eventName] = callbacks
}
}
测试用例
const events = new EventEmitter()
events.on("newListener", function(eventName){
console.log(`eventName`, eventName)
})
events.on("hello", function(){
console.log("hello");
})
let cb = function(){
console.log('cb');
}
events.on("hello", cb)
events.off("hello", cb)
function once(){
console.log("once");
}
events.once("hello", once)
events.off("hello", once)
events.emit("hello")
events.emit("hello")
完整的代码
class EventEmitter{
constructor(){
this._events = {};
}
on(eventName, callback){
if(this._events[eventName]){
if(this.eventName !== "newListener"){
this.emit("newListener", eventName)
}
}
const callbacks = this._events[eventName] || [];
callbacks.push(callback);
this._events[eventName] = callbacks
}
emit(eventName, ...args){
const callbacks = this._events[eventName] || [];
callbacks.forEach(cb => cb(...args))
}
once(eventName, callback){
const one = (...args)=>{
callback(...args)
this.off(eventName, one)
}
one.initialCallback = callback;
this.on(eventName, one)
}
off(eventName, callback){
const callbacks = this._events[eventName] || []
const newCallbacks = callbacks.filter(fn => fn != callback && fn.initialCallback != callback /* 用于once的取消订阅 */)
this._events[eventName] = newCallbacks;
}
}
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!