持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第20天,点击查看活动详情
一个小而巧的发布订阅库,用on去监听事件,用emit去触发事件执行,就像window.addEventLisener
介绍
事件的发布订阅
var emitter = require('tiny-emitter/instance');
emitter.on('some-event', function (arg1, arg2, arg3) {
//
});
emitter.emit('some-event', 'arg1 value', 'arg2 value', 'arg3 value');
on(event, callback[, context])
监听事件
Subscribe to an event
event- the name of the event to subscribe tocallback- the function to call when event is emittedcontext- (OPTIONAL) - the context to bind the event callback to
once(event, callback[, context])
只监听一次的事件
Subscribe to an event only once
event- the name of the event to subscribe tocallback- the function to call when event is emittedcontext- (OPTIONAL) - the context to bind the event callback to
off(event[, callback])
关闭某事件的监听
Unsubscribe from an event or all events. If no callback is provided, it unsubscribes you from all events.
event- the name of the event to unsubscribe fromcallback- the function used when binding to the event
emit(event[, arguments...])
触发事件
Trigger a named event
event- the event name to emitarguments...- any number of arguments to pass to the event subscribers
场景
和vuex类型,可以用来跨组件传递事件和参数
参考
tiny-emitter
//一个构造函数
function E () {
// Keep this empty so it's easier to inherit from
// (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)
// this.e={'some-event':[{fn:function(){},ctx:undefinded}]}
//
}
E.prototype = {
//事件监听,
on: function (name, callback, ctx) {
var e = this.e || (this.e = {});
//在构造函数里增加变量e,存储函数名和相应事件
(e[name] || (e[name] = [])).push({
fn: callback,
ctx: ctx
});
return this;
},
once: function (name, callback, ctx) {
var self = this;
//定义一个函数listener,包含去除监听和执行一次回调
function listener () {
//把变量e里name对应的监听事件listener去掉
self.off(name, listener);
//执行一次这个监听函数
callback.apply(ctx, arguments);
};
// 加这个似乎是为了加个标志,在off去掉监听时,不让被off去除
listener._ = callback
//给name绑定上这个listener, 触发时里面会去掉这个listener,并且执行一次listener里的监听函数
return this.on(name, listener, ctx);
},
emit: function (name) {
//去掉事件名,取出参数数组
var data = [].slice.call(arguments, 1);
//取出这个事件名对应的监听函数数组
var evtArr = ((this.e || (this.e = {}))[name] || []).slice();
var i = 0;
var len = evtArr.length;
//遍历执行里面的函数
for (i; i < len; i++) {
evtArr[i].fn.apply(evtArr[i].ctx, data);
}
return this;
},
//传入要去掉监听的函数,name里的对应函数
off: function (name, callback) {
var e = this.e || (this.e = {});
var evts = e[name];
var liveEvents = [];
//这里我在手写的时候一般用的splice,这里用的其他的方法
// 这里收集name里函数数组里非去除监听的函数
if (evts && callback) {
for (var i = 0, len = evts.length; i < len; i++) {
if (evts[i].fn !== callback && evts[i].fn._ !== callback)
liveEvents.push(evts[i]);
}
}
// Remove event from queue to prevent memory leak
// Suggested by https://github.com/lazd
// Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910
// 如果去除监听函数后数组空了就干脆删掉这个name
(liveEvents.length)
? e[name] = liveEvents
: delete e[name];
return this;
}
};
module.exports = E;
module.exports.TinyEmitter = E;
为什么他不用splice去做off
- 之前的off函数用的是splice
var e =this.e||{}
var evts = e[name]
for(var i=0,len=evts.length;i<len;i++){
if(evts[i] &&evts[i].fn !== callback) continue
evts.splice(i,1)
}
这里有个问题,看例子 github.com/scottcorgan…
emitter.on('test', fn);
emitter.on('test', fn);
emitter.on('test', fn2);
emitter.off('test', fn);
emitter.emit('test');
这里虽然用off去掉了fn,但是仍然会执行一次fn,这就是在for里用splice的弊端,删除数组一项,监听函数数组里的函数index也都挪一位,导致evts遍历的时候漏了一位
你用了splice,那就要判断evts[i]是否为真,而且监听函数数组里的函数index也都挪一位,当然也是可以补救的,我这应该没补错
//删多少补多少
var e =this.e||{}
var evts = e[name]
for(var i=0,len=evts.length;i<len;i++){
if(evts[i] &&evts[i].fn !== callback) continue
evts.splice(i,1)
i--
}
独家特别版
class Observe{
constructor(){
this.map = new Map()
}
on(name,fn){
//以后还是写成this.map[name]吧
if(!this.map.name){this.map.name=[]}
this.map.name.push(fn)
return ()=>{
this.map.name=this.map.name.filter(i=>i!==fn)
//this.map.name.splice(this.map.name.indexOf(fn),1)
}
};
emit(name,data){
if(this.map.name){
this.map.name.forEach(i=>i(data))
}
};
off(name,fn){
if(this.map.name){
this.map.name = this.map.name.filter(i=>i!==fn)
}
}
once(name,fn){
let listener = ()=>{
this.off(name,listener)
fn.apply(this,arguments)
}
this.on(name,listener)
}
}
let emitter = new Observe()
let xx = function(data){
console.log('xx',data)
}
// emitter.on('ss',xx)
// emitter.off('ss',xx)
//这里学习下redux-reducer
let off = emitter.on('ss',xx)
off()
emitter.emit('ss','xx')