一道題目
看到一個很有趣的題目:實現一個方法,支援鏈式呼叫。
lazyman.lazy('Lisa').sleep(3).sleepFirst(4).eat('lunch');
// 4s後輸出:Sleep Afater 4
// 輸出:I'm Lisa
// 3s後輸出:Sleep After 3
// 輸出:I'm eat lunch
解法
話不多說,直接上程式碼:
class LazyMan {
callbacks = [];
constructor() {
this.next();
}
next() {
setTimeout(() => {
const firstFn = this.callbacks.shift();
firstFn && firstFn();
}, 0);
}
lazy(name) {
this.callbacks.push(() => {
console.log(`Hi, I'm ${name}`);
this.next();
});
return this;
}
sleep(time) {
this.callbacks.push(() => {
setTimeout(() => {
console.log(`Sleep after ${time}`);
this.next();
}, time * 1000);
});
return this;
}
sleepFirst(time) {
this.callbacks.unshift(() => {
setTimeout(() => {
console.log(`Sleep after ${time}`);
this.next();
}, time * 1000);
});
return this;
}
eat(item) {
this.callbacks.push(() => {
console.log(`I am eat ${item}`);
this.next();
});
return this;
}
}
const lazyman = new LazyMan();
lazyman.lazy('Lisa').sleep(3).sleepFirst(4).eat('lunch');
題解分析
這個題目,首先要知道如何完成鏈式呼叫,就是設定一個類,類中宣告的方法的結尾最後都會重新返回該類例項的引用,這樣就能夠鏈式呼叫了。
所以我們建立一個LazyMan的類。
接下來是如何保證順序執行,那就是使用一個回撥陣列,每個函式呼叫後會檢查一次回撥是否為空,如果不為空,則繼續執行。
同時為了保證第一次執行前,會先進行一遍所有函式的遍歷,確認優先順序,我們在constructor裡面使用setTimeout進行建立一個微任務,這樣會等main函式裡的巨集任務全部執行完,才會開始真正的函式執行。
所以這個題目的關鍵點:
- callbacks: 儲存函式執行的列表,方便調整執行順序。
- class:建立一個類,為每個函式提供統一的父親,通過return this保證鏈式呼叫。
- next:分步執行函式,保證確認好順序後,所有的函式都會被依次執行。
setTimeout:
- 定時器。
- 建立一個微任務,保證在main函式遍歷後再開始執行。