前言
之前在掘金上到一篇文章关于微信面试的文章,其中提到了手动实现Lazyman的问题。刚开始
看到Lazyman我是一脸懵逼的,这是什么鬼,后来查了查了一下,才发现,其实就是手动实现
以下功能:
实现一个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
以此类推。
看起来好像挺有趣的,我们来分析以下
分析
首先我们看到例子有几个特点,一个是我们调用Lazyman的时候不需要用到new关键字,这意味着我们需要使用工厂函数;另一个是要我们实现链式调用。
我们看到LazyMan(“Hank”).eat(“dinner”).eat(“supper”)
这样的形式,无疑是链式调用了;还有一个难点就是 LazyMan(“Hank”).sleepFirst(5).eat(“supper”)
当存在sleepFirst
时,我们还要先等待一段时间,然后再开始报名字,这就说明sleepFirst
优先级更高,不管何时注册,都要第一个执行,仔细想想
有什么可以实现这个呢?明显我们需要一个任务队列,而且sleepFirst
放在最前面,然后等所有任务都安排好了,才开始执行任务队列
恩?那说明执行任务不能紧跟在插入任务全程的后面,那我们见他们分进两个事件队列就好了,这就需要借助setTimeout
函数了;
除此之外,一个任务完成了,我们怎么通知任务队列去取下一个任务呢?这就需要一个尾调用。
编码
经过上面的分析,我们可以开始编码了:
首先,我们先写工厂函数
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
的时候,我们注册一个监听函数,然后当任务
执行的时候再发布这个事件,让对应的监听函数执行,这样就实现了解耦了
总结
一个小小的Lazyman,竟然有如此多的考点,是在让人受益匪浅,当然,Lazyman还可以使用promise的方式实现,当然实现一个手写的Promise实在有点难(逃),有机会再用promise
实现一次哈
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。