基于单例实现发布订阅者模式
先定义存储事件的数据结构,我们选取一下的这种数据结构。
{
'click': [fn1, fn2, fn3],
'change': [fn1, fn2, fn3]
}
使用方式,以及效果
const fn1 = function(data) {
console.log(data)
}
const fn2 = function(data) {
console.log(data)
}
我们希望$ 触发 'xxx' 事件的时候调用 fn1 这个函数。并且把参数传递 fn1
$.on('xxx', fn1)
触发已经注册好的 'xxx' 事件函数的调用
$.emit('xxx', {name: '小明'})
我们希望移除 'xxx' 事件的 fn1 注册的事件回调函数 。
$.off('xxx', fn1)
var subscribe = (function () {
// 定义事件池
let pond = {};
// 事件池中方法的管理, 把函数按照事件类型加入到池子中去。
const on = function (type, fn) {
// 先判断有没有对应的事件类型,没有的话默认给空数组
!pond.hasOwnProperty(type) ? (pond[type] = []) : null;
let arr = pond[type];
// 验证添添加对应的类型有没有重复的,如果有重复的就不执行添加操作
for (let i = 0; i < arr.length; i++) {
if (arr[i] === fn) {
return;
}
}
// 等价的
// arr.includes(fn) return
arr.push(fn);
};
// 触发事件池中对应的方法
const emit = function (type, ...params) {
// 依次执行对应类型的事件函数中的方法,并把参数传进去
let arr = pond[type] || [];
// 注意点
// 方法1、为了防止在触发方法的过程中删除事件池中的方法,所以克隆一份来执行
arr = arr.slice(0)
for (let i = 0; i < arr.length; i++) {
arr[i](params);
}
};
// 取消事件
const off = function (type, fn) {
let arr = pond[type] || [];
for (let i = 0; i < arr.length; i++) {
if (arr[i] === fn) {
// 从中移除
arr.splice(i, 1);
}
}
};
return {
on,
emit,
off,
};
})();
测试代码
<style>
#box {
width: 100px;
height: 100px;
background: pink;
}
</style>
<div id="box"></div>
const fn1 = function (params) {
console.log(1, params);
};
const fn2 = function (params) {
console.log(2, params);
subscribe.off("A", fn1);
subscribe.off("A", fn2);
};
const fn3 = function (params) {
console.log(3, params);
};
const fn4 = function (params) {
console.log(4, params);
};
const fn5 = function (params) {
console.log(5, params);
};
subscribe.on("A", fn1);
subscribe.on("A", fn2);
subscribe.on("A", fn3);
subscribe.on("A", fn4);
subscribe.on("A", fn5);
let box = document.querySelector("#box");
box.onclick = function () {
subscribe.emit("A", 1, 2);
};
数组塌陷问题
var subscribe = (function () {
// 定义事件池
// {
// xxx: [function(){}, function(){}]
// }
let pond = {};
// 事件池中方法的管理, 把函数按照事件类型加入到池子中去。
const on = function (type, fn) {
// 先判断有没有对应的事件类型,没有的话默认给空数组
!pond.hasOwnProperty(type) ? (pond[type] = []) : null;
let arr = pond[type];
// 验证添添加对应的类型有没有重复的,如果有重复的就不执行添加操作
for (let i = 0; i < arr.length; i++) {
if (arr[i] === fn) {
return;
}
}
// 等价的
// arr.includes(fn) return
arr.push(fn);
};
// 触发事件池中对应的方法
const emit = function (type, ...params) {
console.log('hihi')
// 依次执行对应类型的事件函数中的方法,并把参数传进去
let arr = pond[type] || [];
for (let i = 0; i < arr.length; i++) {
// 是函数的话再执行
if (typeof arr[i] === "function") {
arr[i](params);
} else {
// 不是函数的话就删除当前位置的元素, 从当前位置开始删除一个元素
arr.splice(i, 1);
// 并且让遍历的下标不往后移动
i--
}
}
console.log(arr)
};
// 取消事件
const off = function (type, fn) {
let arr = pond[type] || [];
for (let i = 0; i < arr.length; i++) {
if (arr[i] === fn) {
// 不改变原始数组长度
arr[i] = null;
}
}
};
return {
on,
emit,
off,
};
})();