JS節流和防抖

事件的觸發權不少時候都屬於用戶,有些狀況下會產生問題:javascript

  • 向後臺發送數據,用戶頻繁觸發,對服務器形成壓力
  • 一些瀏覽器事件:window.onresize、window.mousemove等,觸發的頻率很是高,會形成瀏覽器性能問題

若是你碰到這些問題,那就須要用到函數節流和防抖了。html

1、函數節流(throttle)

函數節流:一個函數執行一次後,只有大於設定的執行週期後纔會執行第二次
有個須要頻繁觸發函數,出於優化性能角度,在規定時間內,只讓函數觸發的第一次生效,後面不生效。java

1.如何實現

其原理是用時間戳來判斷是否已到回調該執行時間,記錄上次執行的時間戳,而後每次觸發 scroll 事件執行回調,回調中判斷當前時間戳距離上次執行時間戳的間隔是否已經到達 規定時間段,若是是,則執行,並更新上次執行的時間戳,如此循環;git

html, body { height: 500%; // 讓其出現滾動條 }
function throttle(fn, delay) { // 記錄上一次函數觸發的時間 var lastTime = 0; return function() { // 記錄當前函數觸發的時間 var nowTime = Date.now(); if (nowTime - lastTime > delay) { // 修正this指向問題 fn.call(this); // 同步時間 lastTime = nowTime; } } } document.onscroll = throttle(function() { console.log('scroll事件被觸發了' + Date.now()) }, 200)

上例中用到了閉包的特性--可使變量lastTime的值長期保存在內存中。github

2.函數節流的應用場景

須要間隔必定時間觸發回調來控制函數調用頻率:瀏覽器

  • DOM 元素的拖拽功能實現(mousemove)
  • 搜索聯想(keyup)
  • 計算鼠標移動的距離(mousemove)
  • Canvas 模擬畫板功能(mousemove)
  • 射擊遊戲的 mousedown/keydown 事件(單位時間只能發射一顆子彈)
  • 監聽滾動事件判斷是否到頁面底部自動加載更多:給 scroll 加了 debounce 後,只有用戶中止滾動後,纔會判斷是否到了頁面底部;若是是 throttle 的話,只要頁面滾動就會間隔一段時間判斷一次

 

實現思路

(經常使用版本)服務器

調用函數時,經過上一次pre和如今now兩個變量,記錄調用時間的頻率,prev-now 若是大於約定的時間,才調用函數。調用函數結束後,把pre設置爲如今的時間。閉包

 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(Math.random());
        }
        window.addEventListener('scroll', throttle(handle, 1000)); 

2、函數防抖(debounce)

防抖函數:一個須要頻繁觸發的函數,在規定時間內,只讓最後一次生效,前面的不生效。app

1.如何實現

其原理就第一次調用函數,建立一個定時器,在指定的時間間隔以後運行代碼。當第二次調用該函數時,它會清除前一次的定時器並設置另外一個。若是前一個定時器已經執行過了,這個操做就沒有任何意義。然而,若是前一個定時器還沒有執行,其實就是將其替換爲一個新的定時器,而後延遲必定時間再執行。dom

<button id='btn'>按鈕</button> <script type="text/javascript"> function debounce(fn, delay) { // 記錄上一次的延時器 var timer = null; return function() { // 清除上一次延時器 clearTimeout(timer) timer = setTimeout(function() { fn.apply(this) }, delay) } } document.getElementById('btn').onclick = debounce(function() { console.log('點擊事件被觸發' + Date.now()) }, 1000) </script>

上例中也用到了閉包的特性--可使變量timer的值長期保存在內存中。

2.函數防抖的應用場景

對於連續的事件響應咱們只須要執行一次回調:

  • 每次 resize/scroll 觸發統計事件
  • 文本輸入的驗證(連續輸入文字後發送 AJAX 請求進行驗證,驗證一次就好)

3、總結

函數節流和函數去抖的核心其實就是限制某一個方法被頻繁觸發,而一個方法之因此會被頻繁觸發,大多數狀況下是由於 DOM 事件的監聽回調,而這也是函數節流以及防抖多數狀況下的應用場景。

參考文章

函數節流與函數防抖

JavaScript 函數節流和函數去抖應用場景辨析

函數節流、函數防抖實現原理分析

相關文章
相關標籤/搜索