以前在掘金上到一篇文章关于微信面试的文章,其中提到了手动实现Lazyman的问题。刚开始
看到Lazyman我是一脸懵逼的,这是什么鬼,后来查了查了一下,才发现,其实就是手动实现
如下功能:javascript
实现一个LazyMan,能够按照如下方式调用: LazyMan(“Hank”)输出: Hi! This is Hank! LazyMan(“Hank”).sleep(10).eat(“dinner”)输出 Hi! This is Hank! //等待10秒.. Wake up after 10 Eat dinner~ LazyMan(“Hank”).eat(“dinner”).eat(“supper”)输出 Hi This is Hank! Eat dinner~ Eat supper~ LazyMan(“Hank”).sleepFirst(5).eat(“supper”)输出 //等待5秒 Wake up after 5 Hi This is Hank! Eat supper 以此类推。
看起来好像挺有趣的,咱们来分析如下java
首先咱们看到例子有几个特色,一个是咱们调用Lazyman的时候不须要用到new关键字,这意味着咱们须要使用工厂函数;另外一个是要咱们实现链式调用。
咱们看到LazyMan(“Hank”).eat(“dinner”).eat(“supper”)
这样的形式,无疑是链式调用了;还有一个难点就是 LazyMan(“Hank”).sleepFirst(5).eat(“supper”)
当存在sleepFirst
时,咱们还要先等待一段时间,而后再开始报名字,这就说明sleepFirst
优先级更高,无论什么时候注册,都要第一个执行,仔细想一想
有什么能够实现这个呢?明显咱们须要一个任务队列,并且sleepFirst
放在最前面,而后等全部任务都安排好了,才开始执行任务队列
恩?那说明执行任务不能紧跟在插入任务全程的后面,那咱们见他们分进两个事件队列就行了,这就须要借助setTimeout
函数了;
除此以外,一个任务完成了,咱们怎么通知任务队列去取下一个任务呢?这就须要一个尾调用。面试
通过上面的分析,咱们能够开始编码了:
首先,咱们先写工厂函数promise
function Lazyman ( name ) { return new _Lazyman ( name ); }
接着咱们开始实现Lazyman:微信
constructor ( name ) { this.tasks = [];//设置任务队列 let task = (name => () => { console.log ( `Hi! This is ${name} !` ); this.next (); }) ( name ); this.tasks.push ( task ); //经过settimeout的方法,将执行函数放入下一个事件队列中,从而达到先注册事件,后执行的目的 setTimeout ( () => { this.next (); }, 0 ); } //尾调用函数,一个任务执行完而后再调用下一个任务 next () { let task = this.tasks.shift (); task && task (); } eat ( food ) { let task = (food => () => { console.log ( `Eat ${food}` ); this.next (); }) ( food ); this.tasks.push ( task ); return this; } sleep ( time ) { let task = (time => () => { setTimeout ( () => { console.log ( `Wake up after ${time} s!` ); this.next (); }, time * 1000 ) }) ( time ); this.tasks.push ( task ); return this; } sleepFirst ( time ) { let task = (time => () => { setTimeout ( () => { console.log ( `Wake up after ${time} s!` ); this.next (); }, time * 1000 ) }) ( time ); this.tasks.unshift ( task );//sleepFirst函数须要最早执行,因此咱们须要在任务队列前面放入,而后再执行后面的任务 return this; } }
经过上面的步骤,咱们就实现了一个简单的Lazyman了函数
上面明明实现了一个Lazyman了呀,还有什么能够改进的?固然有,若是咱们调用eat
的时候,想输出的是Eaaaaaaaaaat ${food}!!!!
,而后输出好饱啊好饱啊
这意味着每次改变,咱们都要去修改eat
这个函数,这就耦合度过高了,这时候,咱们能够采用发布订阅的方式,在调用eat
的时候,咱们注册一个监听函数,而后当任务
执行的时候再发布这个事件,让对应的监听函数执行,这样就实现了解耦了this
一个小小的Lazyman,居然有如此多的考点,是在让人受益不浅,固然,Lazyman还能够使用promise的方式实现,固然实现一个手写的Promise实在有点难(逃),有机会再用promise
实现一次哈编码