函數防抖是指對於在事件被觸發n秒後再執行的回調,若是在這n秒內又從新被觸發,則從新開始計時,是常見的優化,適用於閉包
等狀況,防止函數過於頻繁的沒必要要的調用。app
用 setTimeout
實現計時,配合 clearTimeout
實現「從新開始計時」。函數
即只要觸發,就會清除上一個計時器,又註冊新的一個計時器。直到中止觸發 wait 時間後,纔會執行回調函數。性能
不斷觸發事件,就會不斷重複這個過程,達到防止目標函數過於頻繁的調用的目的。優化
function debounce(func, wait) { let timeout return function () { clearTimeout(timeout) timeout = setTimeout(func, wait) //返回計時器 ID } }
container.onmousemove = debounce(doSomething, 1000);
每當事件被觸發,執行的都是那個被返回的閉包函數。this
由於閉包帶來的其做用域鏈中引用的上層函數變量聲明週期延長的效果,debounce
函數的 settimeout計時器 ID timeout
變量能夠在debounce
函數執行結束後依然留存在內存中,供閉包使用。spa
相比於未防抖時的code
container.onmousemove = doSomething
防抖優化後,指向 HTMLDivElement
的從 doSomething
函數的 this
變成了閉包匿名函數的 this
,前者變成了指向全局變量。
同理,doSomething
函數參數也接收不到 MouseEvent
事件了。對象
function debounce(func, wait) { let timeout return function () { let context = this //傳給目標函數 clearTimeout(timeout) timeout = setTimeout( ()=>{func.apply(context, arguments)} //修復 , wait) } }
相比於 一個週期內最後一次觸發後,等待必定時間再執行目標函數;blog
咱們有時候但願能實現 在一個週期內第一次觸發,就當即執行一次,而後必定時間段內都不能再執行目標函數。
這樣,在限制函數頻繁執行的同時,能夠減小用戶等待反饋的時間,提高用戶體驗。
在原來基礎上,添加一個是否當即執行的功能
function debounce(func, wait, immediate) { let time let debounced = function() { let context = this if(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() })
function debounce(func, wait, immediate) { let time let debounced = function() { let context = this if(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) } } debounced.cancel = function() { clearTimeout(time) time = null } return debounced }
首先在公共函數文件中註冊debounce
export function debounce(func, delay) { let timer return function (...args) { if (timer) { clearTimeout(timer) } timer = setTimeout(() => { func.apply(this, args) }, delay) } }
而後在須要使用的組件中引入debounce,而且在created生命週期內調用:
created() { this.$watch('searchText', debounce((newSearchText) => { this.getDatas(newSearchText) }, 200)) }
節流:
一個函數執行一次後,只有大於設定的執行週期,纔會執行第二次
//節流函數 function throttle(fn, delay) { //記錄上一次函數觸發的時間 var lastTime = 0; console.log(this) return function () { //記錄當前函數觸發的時間 var nowTime = Date.now(); if (nowTime - lastTime > delay) { //修正this的指向 fn.call(this); console.log(this); lastTime = nowTime; } } } document.onscroll = throttle(function () { console.log('觸發了scroll' + Date.now()) }, 200)
防抖:
有個須要頻繁觸發函數,出於優化性能角度,在規定的時間內,只讓函數觸發的第一次生效,後面的不生效。
function debounce(fn,delay) { //記錄上一次的延時器 var timer = null; return function () { clearTimeout(timer); timer = setTimeout(() => { fn.apply(this); }, delay); } }