点击事件调用匿名函数如何传参_事件发布/订阅模式的简单实现

这是一种广泛应用于异步编程的模式,是回调函数的事件化,常常用来解耦业务逻辑。事件的发布者无需关注订阅的侦听器如何实现业务逻辑,甚至不用关注有多少个侦听器存在。数据通过消息的方式可以灵活的传递。 ——《深入浅出Nodejs》

是的,了解JS的人都清楚,JS中动辄异步,动辄回调函数,尤其是在DOM中绑定事件。而目前广泛用于处理异步编程的方法主要有发布/订阅模式,Promise,async/await。

可是,你只知道传入回调函数,却不知道这中间都经历了什么?你只知道回调函数会在合适的时机被调用,可这又是为什么?为什么他传入之后可以延迟,而不是立即调用?带着这些疑问,看完下面这些代码,相信你会有所收获。

和观察者模式的区别

先看图:

c81d6b4bd491fc3427aa0d37468b7478.png

两种模式都可以用于松散耦合,改进代码管理和潜在的复用,实际使用的时候,不用纠结与到底是什么模式,重要的明白这个思想。

实现

下面我们一起来写一个简易的事件类,用来注册事件或触发事件等:

class Event {    constructor() {        this.callback = {} // 储存事件    }    on () {} // 注册事件    once () {} // 注册只能调用一次的事件    emit () {} // 触发事件    del () {} // 移除指定的回调函数}

首先,为什么回调函数可以延迟调用?因为内部可以将回调函数存储起来,所以我们首先需要一个对象来存放回调函数。

为什么是对象而不是数组?因为你可以注册很多事件呀,每个事件都可以有一个回调数组,存放一系列的回调函数。所以这里使用对象,可以通过不同的属性访问不同的事件队列。

代码本身不多,是因为加了详细的注释:

/** * 注册事件 * @params type[String] 事件类型 * @params fn[Function] 回调函数 */on (type, fn) {    // 首先判断,callback对象有没有该事件的回调数组    if (!this.callback[type]) {        // 如果没有的话就新建一个数组用来存储type事件的回调函数        this.callback[type] = []    }    this.callback[type].push(fn) // 将回调函数fn存入数组    return this // 返回this是为了实现链式调用}/** * 触发事件 * @params type[String] 事件类型 * @params ...params    传入的参数,不限个数 */emit (type, ...params) {    // 遍历执行对应的回调数组,并传入参数    this.callback[type].forEach(fn => fn(...params))    return this}/** * 注册一个只能执行一次的事件 * @params type[String] 事件类型 * @params fn[Function] 回调函数 */once (type, fn) {    if (!this.callback[type]) {        this.callback[type] = []    }    let _this = this // 保存执行环境    // 由于只能执行一次,这里需要做点处理    // 注意该函数是有名字的,因为需要删除。但名字只在函数内部有效    this.callback[type].push(function once (...args) {        fn(...args) // 这里是为了方便emit传参        _this.del(type, once) // 执行一次后删除自己    })    return this // 链式调用}/** * 删除对应的回调函数 * @params type[String] 事件类型 * @params fn[Function] 回调函数 */del (type, fn) {    // 利用filter删除数组中    this.callback[type] = this.callback[type].filter(cb => fn !== cb)    return this}

这样我们就完成了一个自己的事件管理类,下面写点代码测试一下:

let event = new Event()// 新建两个函数let f1 = function (...args) {console.log('参数', ...args)}let f2 = function () {console.log('执行成功!')}event.once('success', f1)         // 注册f1函数,只能执行一次.on('success', f2)           // 注册f2函数.emit('success', 12, 13)     // 触发success,执行所有回调函数.emit('success')             // 这里只会执行f2,f1自动移除了.del('success', f2)          // 要删除函数的话就不能传入匿名函数了.emit('success', 12)         // 没有反应了,全被移除了

最后

怎么样?其实也不难吧。异步编程是JS的重头戏,理解它的原理是很有必要的。但是说到原理,我觉得在演示方面,挑出重点代码就行了。原生方法或者某些成熟的库一般会对参数等做校验,还要捕捉错误,进行错误处理。这样算下来代码量是很大的,不是很利于理解原理。原来就应该是清晰明了嘛,懂了原理不就可以自己拓展了。

另外感兴趣的朋友可以想一想Promise是怎么实现的?为什么它也可以延迟处理?

传送门

【JS】5行代码实现bind函数CSS动画?教你使用障眼法,打造炫酷充电效果JS动画?其实很简单。150行代码,带你制作雪花飞舞特效

完整代码

class Event {    constructor() {        this.callback = {} // 储存事件    }    on (type, fn) {        if (!this.callback[type]) {            this.callback[type] = []        }        this.callback[type].push(fn)        return this    }    once (type, fn) {        if (!this.callback[type]) {            this.callback[type] = []        }        let _this = this        this.callback[type].push(function once (...args) {        fn(...args)            _this.del(type, once) // 执行一次后删除自己        })        return this    }    emit (type, ...params) {        this.callback[type].forEach(fn => fn(...params))        return this    }    del (type, fn) {        this.callback[type] = this.callback[type].filter(cb => fn !== cb)        return this    }}let event = new Event()let f1 = function (...name) {    console.log('我的名字是:', ...name)}let f2 = function () {    console.log('执行成功!')}event.once('success', f1)         // 注册f1函数,只能执行一次.once('success', f2)         // 注册f1函数,只能执行一次.on('success', f2)           // 注册f2函数.emit('success', 12, 13)     // 触发success,执行所有回调函数.emit('success')             // 这里只会执行f2,f1自动移除了.del('success', f2)          // 要删除函数的话就不能传入匿名函数了.emit('success', 12)         // 没有反应了,全被移除了
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值