函式節流和去抖動
阿新 • • 發佈:2019-01-04
使用場景
以下場景往往由於事件頻繁被觸發,因而頻繁執行DOM操作、資源載入等重行為,導致UI停頓甚至瀏覽器崩潰。
- window物件的resize、scroll事件
- 拖拽時的mousemove事件
- 射擊遊戲中的mousedown、keydown事件
- 文字輸入、自動完成的keyup事件
實際上對於window的resize事件,實際需求大多為停止改變大小n毫秒後執行後續處理;而其他事件大多的需求是以一定的0執行後續處理。針對這兩種需求就出現了debounce和throttle兩種解決辦法。
debounce
抖動:如果用手指一直按住一個彈簧,它將不會彈起直到你鬆手為止。 也就是說當呼叫動作n毫秒後,才會執行該動作,若在這n毫秒內又呼叫此動作則將重新計算執行時間。
介面定義:
/**
* 空閒控制 返回函式連續呼叫時,空閒時間必須大於或等於 idle,action 才會執行
* @param idle {number} 空閒時間,單位毫秒
* @param action {function} 請求關聯函式,實際應用需要呼叫的函式
* @return {function} 返回客戶呼叫函式
*/
debounce(idle,action)
簡單實現1:
利用定時器,讓函式執行延遲500毫秒,在500毫秒內如果有函式又被呼叫則刪除上一次呼叫,這次呼叫500毫秒後執行,如此往復
window.onscroll = function(){//lazyload(); debounce(lazyload,window); }; function debounce(method,context){ clearTimeout(method.timeout); method.timeout = setTimeout(function(){ method.call(context); },500); } function lazyload(){ console.log("scroll執行了"+scrollnum); }
簡單實現2:
還有一種節流方式,是通過返回閉包的形式,可以設定延遲時間,兩者執行的結果是一樣,但是我在實際操作的時候設定延遲500時,滾動過了一會才執行了,設定為delay為100的時候在視覺上就沒有感覺延遲。而且函式也只滾動了一次。
function debounce1(method,delay){ var timer = null; return function(){ var context = this,args = arguments; clearTimeout(timer); timer = setTimeout(function(){ method.apply(context,args); },delay); } }
throttle
當我一直滾動滑鼠的時候,lazyload函式就會不斷被延遲,這樣只有停下來的時候才會執行,那麼再有些需要及時顯示的情況下,就顯得不那麼友好了(對於實現keyup事件的提示也沒有意義了),所以可以為函式新增一個引數作為到固定間隔必須執行,到了這個時間間隔就必須執行,這個時候就引入了節流:
節流:如果將水龍頭擰緊直到水是以水滴的形式流出,那你會發現每隔一段時間,就會有一滴水流出。也就是會說預先設定一個執行週期,當呼叫動作的時刻大於等於執行週期則執行該動作,然後進入下一個新週期
介面定義:
/**
* 頻率控制 返回函式連續呼叫時,action 執行頻率限定為 次 / delay
* @param delay {number} 延遲時間,單位毫秒
* @param action {function} 請求關聯函式,實際應用需要呼叫的函式
* @return {function} 返回客戶呼叫函式
*/
throttle(delay,action)
簡單實現:
var throttle = function(delay, action){
var last = 0;
return function(){
var curr = +new Date();
if (curr - last > delay){
action.apply(this, arguments);
last = curr ;
}
}
}
簡單實現2:
function throttle2(method, delay, time) {
var timeout,startTime = new Date();
return function() {
var context = this,
args = arguments,
curTime = new Date();
clearTimeout(timeout);// 如果達到了規定的觸發時間間隔,觸發 handler
if (curTime - startTime >= time) {
method.apply(context, args);
startTime = curTime;//沒達到觸發間隔,重新設定定時器
} else {
timeout = setTimeout(method, delay);
}
};