-
Notifications
You must be signed in to change notification settings - Fork 894
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
第 28 题:手写发布订阅 #34
Labels
Comments
// 发布订阅中心, on-订阅, off取消订阅, emit发布, 内部需要一个单独事件中心caches进行存储;
interface CacheProps {
[key: string]: Array<((data?: unknown) => void)>;
}
class Observer {
private caches: CacheProps = {}; // 事件中心
on (eventName: string, fn: (data?: unknown) => void){ // eventName事件名-独一无二, fn订阅后执行的自定义行为
this.caches[eventName] = this.caches[eventName] || [];
this.caches[eventName].push(fn);
}
emit (eventName: string, data?: unknown) { // 发布 => 将订阅的事件进行统一执行
if (this.caches[eventName]) {
this.caches[eventName].forEach((fn: (data?: unknown) => void) => fn(data));
}
}
off (eventName: string, fn?: (data?: unknown) => void) { // 取消订阅 => 若fn不传, 直接取消该事件所有订阅信息
if (this.caches[eventName]) {
const newCaches = fn ? this.caches[eventName].filter(e => e !== fn) : [];
this.caches[eventName] = newCaches;
}
}
} |
class EventListener {
listeners = {};
on(name, fn) {
(this.listeners[name] || (this.listeners[name] = [])).push(fn)
}
once(name, fn) {
let tem = (...args) => {
this.removeListener(name, fn)
fn(...args)
}
fn.fn = tem
this.on(name, tem)
}
removeListener(name, fn) {
if (this.listeners[name]) {
this.listeners[name] = this.listeners[name].filter(listener => (listener != fn && listener != fn.fn))
}
}
removeAllListeners(name) {
if (name && this.listeners[name]) delete this.listeners[name]
this.listeners = {}
}
emit(name, ...args) {
if (this.listeners[name]) {
this.listeners[name].forEach(fn => fn.call(this, ...args))
}
}
} |
function deepclone(){ |
|
class EventEmitter {
constructor(){
this.events = {};
}
on(event, callback){
const callbacks = this.events[event] || [];
if(Array.isArray(callbacks)) {
callbacks.push(callback);
this.events[event] = callbacks;
}
return this;
}
off(event, callback){
const callbacks = (this.events[event] || []).filter(cb => cb !== callback);
this.events[event] = callbacks;
return this;
}
once(event, callback){
const wrap = (...args) => {
typeof callback === 'function' && callback.apply(this, args);;
this.off(event, wrap);
}
this.on(event, wrap);
return this;
}
emit(event) {
const callbacks = this.events[event] || [];
if(Array.isArray(callbacks)) {
callbacks.forEach(cb => typeof cb === 'function' && cb());
}
return this;
}
}
const eventEmitter = new EventEmitter();
eventEmitter.on('click', () => {
console.log('click 1')
})
eventEmitter.on('click', () => {
console.log('click 2')
})
// eventEmitter.off('click')
eventEmitter.emit('click')
eventEmitter.once('click')
console.log(eventEmitter);
click 1
click 2
EventEmitter {
events: { click: [ [Function], [Function], [Function: wrap] ] } } |
class Observer {
static events = new Map()
static on(name, fn) {
this.events.set(name, {isOnce: false, fn})
}
static once(name, fn) {
this.events.set(name, {isOnce: true, fn})
}
static off(name) {
this.events.delete(name)
}
static emit(name, data) {
let cache = this.events.get(name)
if (cache) {
if (cache.isOnce) this.events.delete(name)
cache.fn(data)
}
}
} |
class EventBus {
listeners = [];
on(name, fn) {
const listener = {
name,
fn,
isOnce: false,
};
this.listeners.push(listener);
return listener;
}
once(name, fn) {
const listener = {
name,
fn,
isOnce: true,
};
this.listeners.push(listener);
return listener;
}
off(listener) {
const index = this.listeners.findIndex(lst => lst === listener);
if (index > -1) {
this.listeners.splice(index, 1);
}
}
emit(name, data) {
for (const listener of this.listeners) {
if (listener.name === name) {
try {
if (listener.isOnce) this.off(listener);
listener.fn(data);
} catch (error) {
console.error('bus emit error', error);
}
}
}
}
}
const test = new EventBus();
const test1 = test.on('console', () => console.log('123'));
const test2 = test.on('console', () => console.log('456'));
const test3 = test.once('console', () => console.log('789'));
// 第一次
test.emit('console', null);
// 123
// 456
// 789
// 第二次
test.emit('console', null);
// 123
// 456
// 第三次
test.off(test2);
test.emit('console', null);
// 123 |
熟写发布订阅模式可以锻炼你的增删改查能力
class PubSub {
sid = 0
topicList = {}
constructor(...tops) {
this.topicList = Object.fromEntries(tops.map(item => [item, []]));
}
isValidTopic(topic){
return Object.keys(this.topicList).includes(topic)
}
sub(topic, target) {
const isValidTopic = this.isValidTopic(topic);
if(isValidTopic){
target.id = ++this.sid;
target[Symbol.for('update')] = function(cb) {
cb();
}
this.topicList[topic].push(target);
}
}
pub(topic, cb) {
const isValidTopic = this.isValidTopic(topic);
if(isValidTopic){
this.topicList[topic].forEach(o => {
o[Symbol.for('update')](cb);
})
}
}
unsub(topic, target) {
const isValidTopic = this.isValidTopic(topic);
if(isValidTopic){
const i = this.topicList[topic].findIndex(o => o.id == target.id);
if(i != -1) {
this.topicList[topic].splice(i, 1);
}
}
}
}
const pb = new PubSub('china', 'america');
const o1 = {};
const o2 = {};
pb.sub('china', o1);
pb.sub('america', o2);
pb.pub('america', () => {
console.log('对你实施制裁!!!');
})
pb.pub('china', () => {
console.log('强烈反对!!!');
})
pb.unsub('china', o1);
pb.unsub('america', o2);
console.log(pb.topicList); |
function EventEmitter(){
this.caches = {}
}
EventEmitter.prototype.on = function(type, event){
if(!this.caches[type]) this.caches[type] = []
this.caches[type].push(event)
return this
}
EventEmitter.prototype.off = function(type, event){
if(!this.caches[type]) return
this.caches[type] = this.caches[type].filter(item => item !== event)
return this
}
EventEmitter.prototype.once = function(type, event){
var _event = function(){
event.apply(this, arguments)
this.off(type, _event)
}
this.on(type, _event)
return this
}
EventEmitter.prototype.emit = function(){
var type = arguments[0]
if(!this.caches[type]) return
var args = Array.prototype.slice.call(arguments, 1)
for(let event of this.caches[type]){
event.apply(this, args)
}
return this
}
// 使用如下
var e = new EventEmitter();
e.on('log', console.log)
e.on('log', console.log)
e.emit('log', 1, 2, 3, 4)
e.emit('log', 1, 2, 3, 4)
e.off('log', console.log)
e.emit('log', 1, 2, 3, 4)
e.once('log', console.log)
e.emit('log', 1, 2, 3, 4)
e.emit('log', 1, 2, 3, 4) |
//菜鸟一枚,如有出错请多指教
class EventEmitter {
constructor() {
this.events = {};
}
on(eventName, callback) {
if (!this.events[eventName]) {
this.events[eventName] = [callback];
} else {
this.events[eventName].push(callback);
}
}
emit(eventName) {
this.events[eventName] && this.events[eventName].forEach((cb) => cb());
}
}
var e = new EventEmitter();
function workDay() {
console.log("每天工作");
}
function makeMoney() {
console.log("赚100万");
}
function sayLove() {
console.log("向喜欢的人示爱");
}
e.on("money", makeMoney);
e.on("work", workDay);
e.on("love", sayLove);
e.emit("money");
e.emit("work");
e.emit("love"); |
class EventEmitter {
constructor(){
this.events = {}
}
on(eventName, callback){
const callbacks = this.events[eventName]
if( callbacks ){
callbacks.add(callback)
} else {
this.events[eventName] = new Set([callback])
}
return this.events
}
once(eventName, callback){
const onceCallback = (...args)=>{
callback(...args)
this.off(eventName, onceCallback)
}
this.on(eventName, onceCallback)
return this.events
}
emit(eventName){
this.events[eventName] && this.events[eventName].forEach(f=>f());
return this.events
}
off(eventName, callback){
this.events[eventName] && this.events[eventName].delete(callback)
return this.events
}
} |
这里对于监听多次的情况是否未处理 |
订阅和发布的关键在利用一个对象或者map存储事件对象,on是添加事件,off是删除事件,emit是遍历事件对象然后调用 class EventEmitter {
constructor() {
this.event = {};
}
on(eventName, callbackFunc) {
const callback = this.event[eventName] || [];
if (Array.isArray(callback)) {
callback.push(callbackFunc);
this.event[eventName] = callback;
}
return this.event;
}
off(eventName, callback) {
if (this.event[eventName]) {
const newEventCallback = this.event[eventName].filter(c => c !== callback);
this.event[eventName] = newEventCallback;
}
return this.event;
}
emit(eventName, data) {
if (this.event[eventName]) {
this.event[eventName].forEach(callback => {
callback && callback(data);
});
}
return this.event;
}
}
const eventEmitter = new EventEmitter();
const clickFunc = function () {
console.log('click 1');
}
eventEmitter.on('click', clickFunc)
eventEmitter.on('click', () => {
console.log('click 2')
})
eventEmitter.emit('click')
eventEmitter.off('click', clickFunc);
eventEmitter.emit('click') |
class eventEmitter {
list = {}
on(event, fn) {
(this.list[event] || (this.list[event] = [])).push(fn)
return this
}
off(event, fn) {
const curFns = this.list[event] || []
if (!curFns.length) {
return false
}
for (const key of Object.keys(curFns)) {
const val = curFns[key]
if (val === fn || val.fn === fn) {
curFns.splice(key, 1)
break
}
}
return this
}
once(event, fn) {
const on = () => {
fn.apply(this, arguments)
this.off(event, on)
}
this.on(event, on)
return this
}
emit() {
const curEvent = Array.prototype.shift.apply(arguments)
const curFns = this.list[curEvent]
if (!curFns.length) {
return false
}
for (const val of curFns) {
val.apply(this, arguments)
}
}
}
const eventEmit = new eventEmitter()
eventEmit.on('article1', user3).emit('article1', 'test111');
function user3(content) {
console.log('用户3订阅了:', content);
} |
class EventListener {
constructor() {
this.events = {};
}
on(e, cb) {
const { events } = this;
(events[e] || (events[e] = [])).push(cb);
}
once(e, cb) {
const once = (...args) => {
cb(...args);
this.off(e, once);
};
once.fn = cb;
this.on(e, once);
}
off(e, cb) {
const cbs = this.events[e] || [];
let item;
let i = cbs.length;
while (i--) {
item = cbs[i];
// 兼容移除once事件
if (cb === item || cb === item.fn) {
cbs.splice(i, 1);
break;
}
}
}
emit(e, ...args) {
const { events } = this;
(events[e] || []).forEach((cb) => cb(...args));
}
}
// 测试
const eventer = new EventListener();
const f1 = () => console.log(111);
const f2 = () => console.log(222);
eventer.on("click", f1);
eventer.once("click", f2);
eventer.off("click", f2);
eventer.emit("click"); |
你好。我已收到你的邮件。谢谢。
—————————————————
黄镜明
|
class Observer {
constructor() {
this.eventsMap = {}
}
on(type, callback) {
if (!this.eventsMap[type]) {
this.eventsMap[type] = []
}
this.eventsMap[type].push({once: false, callback})
}
once(type, callback) {
if (!this.eventsMap[type]) {
this.eventsMap[type] = []
}
this.eventsMap[type].push({once: true, callback})
}
emit(type, payload) {
let queue = this.eventsMap[type]
let index = 0;
while (index < queue.length && queue) {
const depObj = [...queue].shift()
if (depObj.once) {
depObj.callback(payload)
queue.splice(index, 1)
} else {
depObj.callback(payload)
}
index++
}
}
}
const ev = new Observer()
ev.on('change', function(res) {
console.log(res)
})
// ev.emit('change', 10)
// ev.emit('change', 10)
// ev.emit('change', 10)
ev.once('input', function(res) {
console.log(res)
})
ev.emit('input', 20)
ev.emit('input', 20)
ev.emit('input', 20) |
/**
* 手写发布订阅
* 比如vue的自定义事件
* 父组件 v-on:('update',(e)=>{console.log(e)})
* 子组件 $emit('update', {data:1})
*
* 发布订阅是一个类
* 一般有4个方法
* $on: 入参为一个事件名,一个回调函数
* $emit: 入参为一个事件名,一个传给回调函数的数据
* $once: 与$on类似,但是触发一次回调后就被销毁
* $off: 入参为一个事件名,一个对应的$on注册时的回调function
*/
class EventEmitter {
constructor() {
this.eventHub = {};
}
$on(name, fn) {
if (!this.eventHub[name]) {
this.eventHub[name] = [];
}
this.eventHub[name].push(fn);
}
$emit(name, ...data) {
if (this.eventHub[name]) {
this.eventHub[name].forEach((fn) => {
fn(...data);
});
}
}
$off(name, fn) {
if (!this.eventHub[name]) {
return;
}
if (fn === undefined) {
this.eventHub[name] = [];
} else {
this.eventHub[name] = this.eventHub[name].filter((cb) => cb !== fn);
}
}
$once(name, fn) {
const wrap = (...args) => {
this.$off(name, wrap);
fn(...args);
};
this.$on(name, wrap);
}
} |
你好。我已收到你的邮件。谢谢。
—————————————————
黄镜明
|
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
欢迎在下方发表您的优质见解
The text was updated successfully, but these errors were encountered: