为了用户调用函数时更方便和灵活,所以我们定义的参数需要不固定数据类型,比如像这样:
mylibs.on('event',fn);
mylibs.on({
'event1':fn1,
'event2':fn2,
'event3':fn3
});
mylibs.on('event1 event2 event3',fn);
这是一个自定义事件的例子,有三种传参的方式:1、事件名+回调 2、传递一个对象包含事件名和回调 3、多个事件名+一个回调
你觉得这个函数怎么写才好呢,也许大多数人首先想到的是统一数据类型再处理具体的逻辑,就像下面这样:
//匹配任何空白符,包括\n,\r,\f,\t,\v等(换行、回车、空格、tab等) var eventSplitter = /\s+/; var mylibs = { on:function(name, callback, context){ if(typeof name === 'string'){ name = {name:callback}; } if (eventSplitter.test(name)) { var names = name.split(eventSplitter), name = {}; for (var i = 0, length = names.length; i < length; i++) { name[names[i]] = callback; } } //统一好了数据类型则执行循环(默认数据类型是对象,所以在上面不做判断) for(var key in name){ //具体逻辑...... } return this; } };
这种写法看似不错,先统一处理数据类型,解决了参数不同的问题。不过函数看上去稍显臃肿,如果参数再增加几种类型,那么相应的也会在函数体内增加判断语句,并且把具体的逻辑代码放在for循环内也不是一种优雅的做法。
我们可以尝试一下把判断语句提炼出来,写在一个单独的函数里。
var mylibs = { on:function(name, callback, context){ name = this.eventsApi(name,callback); for(var key in name){ //具体逻辑...... } return this; }, eventsApi:function(name,callback){ if(typeof name === 'string'){ return {name:callback}; } if (eventSplitter.test(name)) { var names = name.split(eventSplitter), name = {}; for (var i = 0, length = names.length; i < length; i++) { name[names[i]] = callback; } return name; } return name; } };
创建了eventsApi函数来对数据类型做统一处理,很明显on函数已经不臃肿了,2个函数各司其职。但是on函数的具体逻辑代码仍然在for循环内,我们得想办法把它抽离出来。
解决的办法可以利用递归,我们先处理一下eventsApi函数。
eventsApi:function(obj,action,name,rest){ if (!name) return true; if(typeof name === 'object'){ for(var key in name){ obj[action].apply(obj,[key,name[key]].concat(rest)); } return false; } if (eventSplitter.test(name)) { var names = name.split(eventSplitter); for (var i = 0, length = names.length; i < length; i++) { obj[action].apply(obj, [names[i]].concat(rest)); } return false; } return true; }
是不是眼前一亮,eventsApi函数并没有统一处理数据类型,而是直接调用on函数,此时on函数变成了一个固定数据类型的函数了,其实就是上面第一种传参的形式:一个事件名+回调。
我们看一下完整的代码加深理解吧。
//匹配任何空白符,包括\n,\r,\f,\t,\v等(换行、回车、空格、tab等) var eventSplitter = /\s+/; var mylibs = { on:function(name, callback, context){ if (!this.eventsApi(this, 'on', name, [callback, context]) || !callback) return this; //具体逻辑...... return this; }, eventsApi:function(obj,action,name,rest){ if (!name) return true; if(typeof name === 'object'){ for(var key in name){ obj[action].apply(obj,[key,name[key]].concat(rest)); } return false; } if (eventSplitter.test(name)) { var names = name.split(eventSplitter); for (var i = 0, length = names.length; i < length; i++) { obj[action].apply(obj, [names[i]].concat(rest)); } return false; } return true; } }; //调用: mylibs.on('event',fn); mylibs.on({ 'event1':fn1, 'event2':fn2, 'event3':fn3 }); mylibs.on('event1 event2 event3',fn);