函數防抖是指對於在事件被觸發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
有兩個做用:
callNow
爲 true
,目標函數能夠在新的週期裏被觸發時被執行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 }