前言
據說阿里有一道面試題就是談談函式節流和函式防抖。 糟了,這可觸碰到我的知識盲區了,好像聽也沒聽過這2個東西,痛定思痛,趕緊學習學習。here we go!
概念和例子
函式防抖(debounce)
在事件被觸發n秒後再執行回撥,如果在這n秒內又被觸發,則重新計時。
看一個?(栗子):
//模擬一段ajax請求
function ajax(content) {
console.log('ajax request ' + content)
}
let inputa = document.getElementById('unDebounce')
inputa.addEventListener('keyup', function (e) {
ajax(e.target.value)
})
複製程式碼
看一下執行結果:
可以看到,我們只要按下鍵盤,就會觸發這次ajax請求。不僅從資源上來說是很浪費的行為,而且實際應用中,使用者也是輸出完整的字元後,才會請求。下面我們優化一下:
//模擬一段ajax請求
function ajax(content) {
console.log('ajax request ' + content)
}
function debounce(fun, delay) {
return function (args) {
let that = this
let _args = args
clearTimeout(fun.id)
fun.id = setTimeout(function () {
fun.call(that, _args)
}, delay)
}
}
let inputb = document.getElementById('debounce')
let debounceAjax = debounce(ajax, 500)
inputb.addEventListener('keyup', function (e) {
debounceAjax(e.target.value)
})
複製程式碼
看一下執行結果:
可以看到,我們加入了防抖以後,當你在頻繁的輸入時,並不會傳送請求,只有當你在指定間隔內沒有輸入時,才會執行函式。如果停止輸入但是在指定間隔內又輸入,會重新觸發計時。 再看一個?:
let biu = function () {
console.log('biu biu biu',new Date().Format('HH:mm:ss'))
}
let boom = function () {
console.log('boom boom boom',new Date().Format('HH:mm:ss'))
}
setInterval(debounce(biu,500),1000)
setInterval(debounce(boom,2000),1000)
複製程式碼
看一下執行結果:
這個?就很好的解釋了,如果在時間間隔內執行函式,會重新觸發計時。biu會在第一次1.5s執行後,每隔1s執行一次,而boom一次也不會執行。因為它的時間間隔是2s,而執行時間是1s,所以每次都會重新觸發計時
個人理解 函式防抖就是法師發技能的時候要讀條,技能讀條沒完再按技能就會重新讀條。
函式節流(throttle)
規定在一個單位時間內,只能觸發一次函式。如果這個單位時間內觸發多次函式,只有一次生效。
看一個?:
function throttle(fun, delay) {
let last, deferTimer
return function (args) {
let that = this
let _args = arguments
let now = +new Date()
if (last && now < last + delay) {
clearTimeout(deferTimer)
deferTimer = setTimeout(function () {
last = now
fun.apply(that, _args)
}, delay)
}else {
last = now
fun.apply(that,_args)
}
}
}
let throttleAjax = throttle(ajax, 1000)
let inputc = document.getElementById('throttle')
inputc.addEventListener('keyup', function(e) {
throttleAjax(e.target.value)
})
複製程式碼
看一下執行結果:
可以看到,我們在不斷輸入時,ajax會按照我們設定的時間,每1s執行一次。
結合剛剛biubiubiu的?:
let biubiu = function () {
console.log('biu biu biu', new Date().Format('HH:mm:ss'))
}
setInterval(throttle(biubiu,1000),10)
複製程式碼
不管我們設定的執行時間間隔多小,總是1s內只執行一次。
個人理解 函式節流就是fps遊戲的射速,就算一直按著滑鼠射擊,也只會在規定射速內射出子彈。
總結
- 函式防抖和函式節流都是防止某一時間頻繁觸發,但是這兩兄弟之間的原理卻不一樣。
- 函式防抖是某一段時間內只執行一次,而函式節流是間隔時間執行。
結合應用場景
- debounce
- search搜尋聯想,使用者在不斷輸入值時,用防抖來節約請求資源。
- window觸發resize的時候,不斷的調整瀏覽器視窗大小會不斷的觸發這個事件,用防抖來讓其只觸發一次
- throttle
- 滑鼠不斷點選觸發,mousedown(單位時間內只觸發一次)
- 監聽滾動事件,比如是否滑到底部自動載入更多,用throttle來判斷
擴充
這是高程中的經典程式碼:
function throttle(method, context) {
clearTimeout(method.tId);
method.tId = setTimeout(function () {
method.call(context);
}, 100)
}
複製程式碼
我們通過上面的例子知道,其實這段函式應該是debounce函式防抖,而不是函式節流,很多文章也都會拿這段程式碼來做例子,函式本身沒錯,但是命名錯了。
原作者的這段話就寫的很好,
就以 throttle 為例,某日,老師給你佈置了一個作業,讓你深入理解一下 throttle,第二天上課來聊聊。張三心裡非常高興,這個概念在經典書籍《JavaScript高階程式設計》中見過,開啟一看,就兩頁,而且解釋地非常清晰,看完就高興地幹別的事情去了。而李四,覺得高程三講的有點少,而去谷歌了下其他關於 throttle 的知識點,興奮地看到 throttle 函式的好幾種寫法,發現高程三隻是用了最簡單的方式,還有更優雅運用場景更多的寫法,或許此時他已經發現和 throttle 同時出現的還有個 debounce,這是什麼鬼?反正老師沒說,以後再看吧,於是心滿意足地玩遊戲去了。而王五,和李四一樣發現了 debounce,這是什麼?一起了解了吧,繼而發現 debounce 的用法居然和高程三中的 throttle 一樣!繼續挖下去,發現高程三中的 throttle 函式其實應該叫 debounce,看到最後,王五已經把 throttle 和 debounce 徹底理解了。
我們要做王五,並且爭取早日產出一手知識!加油!
廣而告之
本文釋出於薄荷前端週刊,歡迎Watch & Star ★,轉載請註明出處。