面試之函式節流和函式防抖
從場景說起 滑動到底部繼續載入,是移動端很常見的一種場景。
通常來說,我們會在對可滑動區域(一般是window)的scroll事件做監聽,判斷距離底部還有多遠,如果距離底部較近,則發起HTTP請求,請求下一頁的資料。
很容易寫出這樣的程式碼:
let page = 0; document.querySelector('.main').addEventListener('scroll', () => { scrollMore(this); }) // 滑動獲取更多內容 function scrollMore(el) { if (getScrollTop(el) + getClientHeight() >= getScrollHeight(el) - 100) { fetch('http://api.jd.com/somethings/' + page++) .then(function(response) { console.log(response.json()); // do something 把資料填充到頁面上 }) } } function getScrollTop(el) { //取視窗滾動條高度 return el.scrollTop; } function getClientHeight() { //取視窗可視範圍的高度 let clientHeight = 0; if (document.body.clientHeight && document.documentElement.clientHeight) { clientHeight = (document.body.clientHeight < document.documentElement.clientHeight) ? document.body.clientHeight : document.documentElement.clientHeight; } else { clientHeight = (document.body.clientHeight > document.documentElement.clientHeight) ? document.body.clientHeight : document.documentElement.clientHeight; } return clientHeight; } function getScrollHeight(el) { //取文件內容實際高度 return Math.max(document.body.scrollHeight, el.scrollHeight); }
但是這樣很容易就發現一個問題,觸發的scroll事件太頻繁了,在一些低端機上,可能會卡頓。
其實我們的判斷沒有必要這麼頻繁,使用者的滑動操作是一段時間內不停觸發的。
我們希望在一小段時間內,只觸發一次執行。
函式節流
這就是函式節流要做的事情,《JavaScript高階程式設計》中給了一個方法:
function throttle (method, context) {
clearTimeout(method.tId);
method.tId = setTimeout(function () {
method.call(context);
}, 100)
}
這個程式碼的原理就是在多次執行的時候,清除上一次的timeout,如果時間間隔沒有超過100ms,則又新建一個新的timeout,舊的則被丟棄。
考慮一下,如果在一段時間內,每隔99ms觸發一次,那麼不停地清除上一次的timeout,函式將永遠不會執行。
我們更希望,每隔100ms,函式就執行一次,而不是像現在一樣。
重新思考這個問題。
關鍵點有兩個:
間隔 執行 先來把函式封裝一下,將節流函式return出來:
function throttle1 (method, delay) { let timer = null; return function () { let context = this ; if (timer) clearTimeout(timer); timer = setTimeout(() => { method.call(context); }, delay) } } 加上間隔判定: function throttle2 (method, delay) { let timer = null; let start; return function () { let context = this , current = Date.now() ; if (timer) clearTimeout(timer); if (!start) { start = current; } if (current - start >= delay) { method.call(context); start = current; } else { timer = setTimeout(() => { method.call(context); }, delay) } } }
這樣,基本上完成了一個throttle函式。
函式防抖
防抖的場景也比較常見。
比如搜尋時,監聽使用者輸入,提供聯想詞;或者使用者連續點選提交按鈕的場景。
說實話,我感覺《高階》那本書裡的節流例子,就是一個防抖的版本。
幾乎可以拿來就用。
我們還是做一個比較完善的版本。
function debounce (method, delay) {
let timeout = null;
return function () {
clearTimeout(timeout);
timeout = setTimeout(() => {
method.apply(this, arguments);
}, delay);
};
}
本次給大家推薦一個最後給大家推薦一個免費的學習群,裡面概括移動應用網站開發,css,html,webpack,vue node angular以及面試資源等。 對web開發技術感興趣的同學,歡迎加入Q群:864305860,不管你是小白還是大牛我都歡迎,還有大牛整理的一套高效率學習路線和教程與您免費分享,同時每天更新視訊資料。 最後,祝大家早日學有所成,拿到滿意offer,快速升職加薪,走上人生巔峰。