js 函數防抖

函數防抖是什麼

函數防抖是指對於在事件被觸發n秒後再執行的回調,若是在這n秒內又從新被觸發,則從新開始計時,是常見的優化,適用於閉包

  • 表單組件輸入內容驗證
  • 防止屢次點擊致使表單屢次提交

等狀況,防止函數過於頻繁的沒必要要的調用。app

代碼實現

思路

setTimeout 實現計時,配合 clearTimeout 實現「從新開始計時」。函數

即只要觸發,就會清除上一個計時器,又註冊新的一個計時器。直到中止觸發 wait 時間後,纔會執行回調函數。優化

不斷觸發事件,就會不斷重複這個過程,達到防止目標函數過於頻繁的調用的目的。this

初步實現

function debounce(func, wait) {
    let timeout
    return function () {
        clearTimeout(timeout)
        timeout = setTimeout(func, wait) //返回計時器 ID
    }
}

示意

container.onmousemove = debounce(doSomething, 1000);

註解:關於閉包

每當事件被觸發,執行的都是那個被返回的閉包函數。code

由於閉包帶來的其做用域鏈中引用的上層函數變量聲明週期延長的效果,debounce 函數的 settimeout計時器 ID timeout 變量能夠在debounce 函數執行結束後依然留存在內存中,供閉包使用。對象

優化:修復

相比於未防抖時的事件

container.onmousemove = doSomething

防抖優化後,指向 HTMLDivElement 的從 doSomething 函數的 this 變成了閉包匿名函數的 this ,前者變成了指向全局變量。內存

同理,doSomething 函數參數也接收不到 MouseEvent 事件了。作用域

修復代碼

functiondebounce(func, wait) {let timeout
        returnfunction() {let context = this//傳給目標函數
            clearTimeout(timeout)
            timeout = setTimeout(
                ()=>{func.apply(context, arguments)} //修復
            , wait)
        }
    }

優化:當即執行

相比於 一個週期內最後一次觸發後,等待必定時間再執行目標函數;

咱們有時候但願能實現 在一個週期內第一次觸發,就當即執行一次,而後必定時間段內都不能再執行目標函數。

這樣,在限制函數頻繁執行的同時,能夠減小用戶等待反饋的時間,提高用戶體驗。

代碼

在原來基礎上,添加一個是否當即執行的功能

functiondebounce(func, wait, immediate) {let time
        let debounced = function() {let context = thisif(time) clearTimeout(time)
    
            if(immediate) {
                let callNow = !time
                if(callNow) func.apply(context, arguments)
                time = setTimeout(
                    ()=>{time = null} //見註解
                , wait)
            } else {
                time = setTimeout(
                    ()=>{func.apply(context, arguments)}
                , wait) 
            }
        }
        return debounced
    }

註解

把保存計時器 ID 的 time 值設置爲 null 有兩個做用:

  • 做爲開關變量,代表一個週期結束。使得 callNowtrue,目標函數能夠在新的週期裏被觸發時被執行
  • timeout 做爲閉包引用的上層函數的變量,是不會自動回收的。手動將其設置爲 null ,讓它脫離執行環境,一邊垃圾收集器下次運行是將其回收。

優化:取消當即執行

添加一個取消當即執行的功能。

函數也是對象,也能夠爲其添加屬性。

爲了添加 「取消當即執行」功能,爲 debounced 函數添加了個 cancel 屬性,屬性值是一個函數

debounced.cancel = function() {
            clearTimeout(time)
            time = null
     }

示意:

var setSomething = debounce(doSomething, 1000, true)
    
    container.onmousemove = setSomething
    
    document.getElementById("button").addEventListener('click', function(){
        setSomething.cancel()
    })

完整代碼

functiondebounce(func, wait, immediate) {
        let time
        let debounced = function() {
            let context = this
            if(time) clearTimeout(time)
    
            if(immediate) {
                let callNow = !timeif(callNow) func.apply(context, arguments)
                time = setTimeout(
                    ()=>{time = null} //見註解
                , wait)
            } else {
                time = setTimeout(
                    ()=>{func.apply(context, arguments)}
                , wait) 
            }
        }
    
        debounced.cancel = function() {
            clearTimeout(time)
            time = null
        }
    
        return debounced
    }
相關文章
相關標籤/搜索