實際工做中,經過監聽某些事件,如scroll
事件檢測滾動位置,根據滾動位置顯示返回頂部按鈕;如resize
事件,對某些自適應頁面調整DOM
的渲染;如keyup
事件,監聽文字輸入並調用接口進行模糊匹配等等,這些事件處理函數調用的頻率若是過高,會加劇瀏覽器的負擔,減弱性能,形成用戶體驗很差。此時須要採用debounce
(防抖)和throttle
(節流)的方式來減小調用頻率,同時不影響原來效果。javascript
當持續觸發事件時,一段時間段內沒有再觸發事件,事件處理函數纔會執行一次,若是設定的時間到來以前就觸發了事件,延時從新開始。
函數防抖的應用場景,最多見的就是用戶註冊時候的手機號碼驗證和郵箱驗證了。只有等用戶輸入完畢後,前端才須要檢查格式是否正確,若是不正確,再彈出提示語。前端
上圖中,持續觸發scroll
事件時,並不執行handle
函數,當1000毫秒內沒有觸發scroll
事件時,纔會延時觸發scroll
事件;
上面原理:對處理函數進行延時操做,若設定的延時到來以前,再次觸發事件,則清除上一次的延時操做定時器,從新定時。
代碼以下:java
// 函數防抖 var timer = false; document.getElementById("debounce").onscroll = function(){ clearTimeout(timer); // 清除未執行的代碼,重置回初始化狀態 timer = setTimeout(function(){ console.log("函數防抖"); }, 1000); };
防抖函數的封裝使用瀏覽器
/** * 防抖函數 * @param method 事件觸發的操做 * @param delay 多少毫秒內連續觸發事件,不會執行 * @returns {Function} */ function debounce(method,delay) { let timer = null; return function () { let self = this, args = arguments; timer && clearTimeout(timer); timer = setTimeout(function () { method.apply(self,args); },delay); } } window.onscroll = debounce(function () { let scrollTop = document.body.scrollTop || document.documentElement.scrollTop; console.log('滾動條位置:' + scrollTop); },1000)
另外一種寫法app
// 防抖 function debounce(fn, wait) { var timeout = null; return function() { if(timeout !== null) clearTimeout(timeout); timeout = setTimeout(fn, wait); } } // 處理函數 function handle() { console.log("函數防抖"); } // 滾動事件 window.addEventListener('scroll', debounce(handle, 1000));
當持續觸發事件時,保證必定時間段內只調用一次事件處理函數。
函數節流應用的實際場景,多數在監聽頁面元素滾動事件的時候會用到。函數
上圖中,持續觸發scroll
事件時,並不當即執行handle
函數,每隔1000毫秒纔會執行一次handle
函數;
函數節流的要點是,聲明一個變量當標誌位,記錄當前代碼是否在執行。若是空閒,則能夠正常觸發方法執行。
代碼以下:性能
// 函數節流 定時器 var canRun = true; document.getElementById("throttle").onscroll = function(){ if(!canRun){ // 判斷是否已空閒,若是在執行中,則直接return return; } canRun = false; setTimeout(function(){ console.log("函數節流"); canRun = true; }, 300); };
節流函數的封裝使用this
//節流throttle代碼(時間戳) var throttle = function(func, delay) { var prev = Date.now(); return function() { var context = this; var args = arguments; var now = Date.now(); if (now - prev >= delay) { func.apply(context, args); prev = Date.now(); } } } function handle() { console.log("函數節流"); } window.addEventListener('scroll', throttle(handle, 1000));
//節流throttle代碼(定時器) var throttle = function(func, delay) { var timer = null; return function() { var context = this; var args = arguments; if (!timer) { timer = setTimeout(function() { func.apply(context, args); timer = null; }, delay); } } } function handle() { console.log("函數節流"); } window.addEventListener('scroll', throttle(handle, 1000));
// 節流throttle代碼(時間戳+定時器): var throttle = function(func, delay) { var timer = null; var startTime = Date.now(); return function() { var curTime = Date.now(); var remaining = delay - (curTime - startTime); var context = this; var args = arguments; clearTimeout(timer); if (remaining <= 0) { func.apply(context, args); startTime = Date.now(); } else { timer = setTimeout(func, remaining); } } } function handle() { console.log("函數節流"); } window.addEventListener('scroll', throttle(handle, 1000));
用時間戳+定時器,當第一次觸發事件時立刻執行事件處理函數,最後一次觸發事件後也還會執行一次事件處理函數spa