一.什么是函數防抖&節流
-
函數防抖
函數防抖(debounce):在事件被觸發n秒后再執行回調,如果在這n秒內又被觸發,則重新計時
-
函數節流(throttle):
當持續觸發事件時,在規定時間段內只能調用一次回調函數。
如果在規定時間內又觸發了該事件,則什么也不做,也不會重新計時,即使一直觸發,也是每隔n秒執行一次回調
-
相同點/不同點
相同:都是限制回調函數調用頻率,避免大量計算導致的頁面卡頓.
不同:防抖是將多次執行變為最后一次執行,節流是將多次執行變為每隔一段時間執行一次回調函數.
二.為什么需要函數防抖&節流
前端開發過程中,有一些事件,常見的例如,onresize,scroll,mousemove ,mousehover 等,會被頻繁觸發(短時間內多次觸發),不做限制的話,有可能一秒之內執行幾十次、幾百次,如果在這些函數內部執行了其他函數,尤其是執行了操作 DOM 的函數(瀏覽器操作 DOM 是很耗費性能的),那不僅會浪費計算機資源,還會降低程序運行速度,甚至造成瀏覽器卡死、崩潰。這種問題顯然是致命的。
在例如回調函數里面代碼塊比較沉重時,這樣頻繁觸發,就會大大降低性能了
除此之外,短時間內重復的 ajax 調用不僅會造成數據關系的混亂,還會造成網絡擁塞,增加服務器壓力,顯然這個問題也是需要解決的
三.函數的防抖
-
防抖的思路
函數防抖的要點,是需要一個 setTimeout 來輔助實現,延遲運行需要執行的代碼。如果方法多次觸發,則把上次記錄的延遲執行代碼用 clearTimeout 清掉,重新開始計時。若計時期間事件沒有被重新觸發,等延遲時間計時完畢,則執行目標代碼
既然前面都提到了計時,那實現的關鍵就在于
setTimeOut
這個函數,由于還需要一個變量來保存計時,考慮維護全局純凈,可以借助閉包來實現: -
防抖代碼實現
function debounce(fn, delay) { // 創建一個標記用來存放定時器的返回值 var timer = null; return function (e) { // 每當用戶輸入的時候把前一個 setTimeout clear 掉 clearTimeout(timer); // 然后又創建一個新的 setTimeout, // 樣就能保證interval 間隔內如果時間持續觸發, // 就不會執行 fn 函數 timer = setTimeout(() => { //這個this的修正是為了handle函數里面的this指向事件偵聽對象,就是docment元素, //如果不修正handle函數中的this就指向window //還有這個arguments 是為了參數 e 穿到handle函數中 fn.apply(this, arguments); }, delay); }; } // 處理函數 function handle(e) { this.style.color="red"; console.log(this); console.log('防抖:', Math.random()); } var bn=document.querySelector("button"); bn.addEventListener("click",debounce(handle, 500));
-
應用場景
函數防抖一般用在什么情況之下呢?一般用在,連續的事件只需觸發一次回調的場合。具體有:
兩個條件:
- 如果客戶連續的操作會導致頻繁的事件回調(可能引起頁面卡頓).
- 客戶只關心"最后一次"操作(也可以理解為停止連續操作后)所返回的結果.
例如:
- 搜索框搜索輸入。只需用戶最后一次輸入完,再發送請求;
- 用戶名、手機號、郵箱輸入驗證;
- 給按鈕加函數防抖 防止表單多次提交
- 按鈕點擊:收藏,點贊,心標等
四.函數的節流
-
節流的思路
第一次先設定一個變量true,第二次執行這個函數時,會判斷變量是否true,是則返回。
當第一次的定時器執行完函數最后會設定變量為flase。
那么下次判斷變量時則為flase,函數會依次運行
-
節流代碼的實現
function throttle(fn, delay = 100) { //首先設定一個變量,在沒有執行我們的定時器時為null let timer = null; return function () { //當我們發現這個定時器存在時,則表示定時器已經在運行中,需要返回 if (timer) return; timer = setTimeout(() => { fn.apply(this, arguments); timer = null; }, delay); } } // 處理函數 function handle(e) { console.log(this); console.log('節流:', Math.random()); } var bn = document.querySelector("button"); bn.addEventListener("click", throttle(handle, 500));
-
應用場景
兩個條件:
- 客戶連續頻繁地觸發事件
- 客戶不再只關心"最后一次"操作后的結果反饋.而是在操作過程中持續的反.
例如:
1.onscroll滾動時候的
2.游戲中的刷新率
3.DOM元素拖拽