以前在掘金上到一篇文章關於微信面試的文章,其中提到了手動實現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
實現一次哈編碼