发布/订阅模式

325 阅读1分钟

发布 / 订阅

看起来貌似用数组可以实现,先来试一下

let event={
    list:[],
    on(fn){
        if(this.list.includes(fn))return false
        this.list.push(fn)
    },
    emit(){
        this.list.forEach(cb=>{
            cb()
        })
    }
}

function log(){
    console.log('log')
}
event.on(log)
event.emit(log)

看起来没有问题,但是好像和我们平时使用的不一样呀,应该有事件名,而且按照以上的写法,会执行所有被监听的函数,而不是我们想要触发的事件,改写一下

想要修复以上缺点,就不能用数组来存储,要使用对象

let event={
    list:{},
    on(name,fn){
        if(!this.list[name]){
            this.list[name]=[]
        }
        this.list[name].push(fn)
    },
    emit(){
        let args=[...arguments]
        let name=args.shift()
        let fns=this.list[name]
        fns.forEach(cb=>{
            cb.apply(null,args)
        })
    }
}

function log(eventName){
    console.log(eventName)
}
function log2(eventName){
    console.log(eventName)
}
event.on('print',log)
event.on('print2',log2)
event.emit('print','print')
event.emit('print2','print2')

这样就可以正常工作啦,每个事件互不影响,因为回调函数存储在不同的数组

新增一个取消订阅的方法

let event={
    list:{},
    on(name,fn){
        if(!this.list[name]){
            this.list[name]=[]
        }
        this.list[name].push(fn)
    },
    emit(){
        let args=[...arguments]
        let name=args.shift()
        let fns=this.list[name]
        if(!fns||fns.length===0)return false
        fns.forEach(cb=>{
            cb.apply(null,args)
        })
    },
    remove(name,fn){
        let fns=this.list[name]
        if(!fns)return false
        if(!fn){
            fns.length=0
            return
        }
        fns.forEach((cb,i)=>{
            if(cb===fn){
                fns.splice(i,1)
            }
        })
        return true
    }
}

function log(eventName){
    console.log(eventName)
}
function log2(eventName){
    console.log(eventName)
}
event.on('print',log)
event.on('print2',log2)
event.remove('print2')
event.emit('print','print')
event.emit('print2','print2')