一、前言
以下場景往往由于事件頻繁被觸發,因而頻繁執行DOM操作、資源加載等重行為,導致UI停頓甚至瀏覽器崩潰。
- window對象的resize、scroll事件
- 拖拽時的mousemove事件
- 射擊游戲中的mousedown、keydown事件
- 文字輸入、自動完成的keyup事件
實際上對于window的resize事件,實際需求大多為停止改變大小n毫秒后執行后續處理;而其他事件大多的需求是以一定的頻率執行后續處理。針對這兩種需求就出現了debounce和throttle兩種解決辦法。
二、什么是debounce
1、定義:如果用手指一直按住一個彈簧,它將不會彈起直到你松手為止。也就是說當調用動作n毫秒后,才會執行該動作,若在這n毫秒內又調用此動作則將重新計算執行時間。
/**
* 空閑控制 返回函數連續調用時,空閑時間必須大于或等于 idle,action 才會執行
* @param idle {number} 空閑時間,單位毫秒
* @param action {function} 請求關聯函數,實際應用需要調用的函數
* @return {function} 返回客戶調用函數
*/
function debounce(idle, action){
var last
return function(){
var ctx = this, args = arguments
clearTimeout(last)
last = setTimeout(function(){
action.apply(ctx, args)
}, idle)
}
}
三、什么是throttle
1、定義:如果將水龍頭擰緊直到水是以水滴的形式流出,那你會發現每隔一段時間,就會有一滴水流出。也就是會說預先設定一個執行周期,當調用動作的時刻大于等于執行周期則執行該動作,然后進入下一個新周期。
/**
* 頻率控制 返回函數連續調用時,action 執行頻率限定為 次 / delay
* @param delay {number} 延遲時間,單位毫秒
* @param action {function} 請求關聯函數,實際應用需要調用的函數
* @return {function} 返回客戶調用函數
*/
function throttle(fn, wait) {
let timer;
let previous = 0;
let throttled = function () {
let now = +new Date();
// remaining 不觸發下一次函數的剩余時間
let remaining = wait - (now - previous);
if (remaining < 0) {
if (timer) {
clearTimeout(timer);
timer = null;
}
previous = now;
fn.apply(this, arguments)
} else if (!timer) {
timer = setTimeout(() => {
previous = new Date().getTime();
timer = null;
fn.apply(this, arguments);
}, remaining);
}
}
return throttled;
}
- 節流:在規定時間內,保證執行一次該函數。
- 防抖:當持續觸發事件時,一定時間段內沒有再觸發事件,事件處理函數才會執行一次,如果設定的時間到來之前,又一次觸發了事件,就重新開始延時。