技术 | 前端面试题(二):自定义事件

阅读 6030
收藏 181
2017-02-21
原文链接:mp.weixin.qq.com

我和阿里巴巴的同事守雌将为大家带来一个系列专题:前端面试题解析,一周更新两篇,也许答案可能不是最优的,但是也可以给你提供解决问题的思路。面试题力求实战,期望对于找工作的你,温故而知新的你,能有所帮助。


关于事件,我想这也是前端面试中必问的一道题,除了浏览器实现的原生事件之外,今天这一题是想说一说,如何实现一个自定义事件。

题目:如何利用JavaScript实现一个自定义事件,存在on,off,emit三个方法?

这个题目的意义在哪里?我想,应该是对于一些特定的业务逻辑,比如在注册一个“通知”的事件,在与Native交互之后,假设这个交互是在入口级别的页面里,那么如何发送给具体某个业务呢?事件应该是最简单的一种方式,在某个具体的业务中注册一个事件,然后在与Native交互完,拿到某些数据后,然后触发这个事件。

我们来一步一步实现一个最简单的事件类Event,不考虑任何其他复杂的情况。假设在这个Event类的内部有一个this._events = [] 数组来维系整个事件系统,我们分别实现on,off,emit三个方法即可。

on(注册一个事件):

Event.prototype.on = function(type,fun){
    let cbs = this._events[type];
    cbs ? cbs.push(fun) : this._events[type] = [];
        if (!cbs) {
            this._events[type].push(fun)
    }        
}

这里为什么要将this._events设计为二维数组?因为事件可以是多个,但是事件名可能相同。这个逻辑意图非常的明显,根据type参数从this._events中获取是否存在。如果不存在,创建一个type为key的数组,并将事件句柄程序push到数组中。

off(注销一个事件):

Event.prototype.off = function(type,fun){    
    let cbs = this._events[type];    
    //事件列队中无事件
    if (!cbs) {        
            return this;
    }    
        //删除所有的事件
    if (!event && !fun) {        
            this._events = {};        
            return this;
    }    
        //只有事件名称时
    if (event && !fun) {        
            this._events[type] = null;        
            return this;
    }    
        //删除某个事件队列中的某个事件
    let cb;    
        let i = cbs.length;    
        while(i--){
        cb = cbs[i];        
                if (cb === fun || cb.fun === fun) {             
                    cbs.splice(i,1);             
                    break;
        }
    }
}

虽然注销事件方法的逻辑可能相比之下稍许多了些,但它的实现也非常简单,只要只存在事件组key名的情况,或者删除某个事件队列中的某个事件句柄程序即可。

emit(触发一个事件):

Event.prototype.emit = function(type){    
    let cbs = this._events[type];    
    let args = tools.toArray(arguments,1);    
    if (cbs) {        
        let i = 0;        
        let j = cbs.length;        
        for(;i<j;i++){            
            let cb = cbs[i];            
            cb.apply(this,args);
        }
    }
}

逻辑依然非常简单,通过事件名从this._events获取相应的事件句柄程序数组,然后将arguments转成数组,(这里考虑的是可能会传入参数)如果事件句柄程序数组存在,进行循环,再讲args参数apply给每一个取出来的事件句柄程序。

这又是一个“抛砖引玉”,关于事件,还会有更多的业务实现,比如根据组件来依次传递触发,然后建立一个中间的通道,一边订阅,一边发布,它们都向中间通道来注册事件,当临界点来临时,发布者只向中间通道来发布,订阅者根本不需要关注发布者。

欢迎大家关注象尘说-编程之道“小密圈”,前端面试题解析,会在“小密圈”中讨论其他的优化方案。如果你还不知道“小密圈”有什么作用,可以点击菜单栏上“小密圈”来获取详情。

地址链接:https://wx.xiaomiquan.com/mweb/views/joingroup/join_group.html?group_id=5414881224&secret=ql23uqjlnm80lxm9od6llkqijltb5r3u&extra=3e73c043fd796c547b54675d60d9b7bc343c71590f108862080eb35eb863bb1c


更多精彩内容可关注我的个人微信公众号:搜索fed-talk或者扫描下列二维码,也欢迎您将它分享给自己的朋友。

评论