防抖與節流

前言

在開發中經常遇到一些頻發觸發的事件,比如說鼠標移動,鍵盤的輸入輸出,窗口的縮放,滾動條的運動等等。

下面舉個例子來了解事件的頻繁觸發:

比如,頁面上有一個元素,我們給這個元素(id爲 container)來綁定鼠標移動事件:

let count = 1
let container = document.getElementById("container")

function getUserAction(){
	container.innerHTML = count++;
}

container.onmousemove = getUserAction

這是一段很簡單的代碼,效果就是隨着鼠標在元素上運動而改變元素的內容。雖然在瀏覽器上不會出現什麼卡頓,畢竟邏輯簡單啊~~如果換成複雜的邏輯或者異步操作,那麼就可怕了。

爲了解決這種問題,有兩種方案:

  1. 防抖
  2. 節流

防抖

【原理】:儘管觸發了事件,但是不立馬執行回調,如果你在一個事件觸發的 n 秒內又觸發了這個事件,那就以新的事件的事件爲準,n 秒後再執行回調,總而言之,就是要等你觸發完事件 n 秒內不觸發了再執行。

由上面可知,我們就是需要一個函數來延遲執行 DOM 的回調函數,所以立馬想到的就是 setTimeout

function debounce(fun,wait){
	let timeout
	return function(){
		clearTimeout(timeout)
		timeout = setTimeout(fun,wait)
	}
}

所以,開頭的例子就可以使用這個防抖函數了:

container.onmousemove = debounce(getUserAction,1000)

一旦這麼使用以後,就會擾亂了 this 的指向。在事件的回調函數中,this 指向的是對應的 DOM ,也就是說,需要手動將處理真正業務邏輯的 fun 函數中的 this 指向 DOM

function debounce(fun,wait){
	let timeout
	let context = this
	return function(){
		clearTimeout(timeout)
		timeout = setTimeout(function(){
			fun.call(context)
		},wait)
	}
}

除了 this 以外,還需要注意的是事件對象,同樣需要修改:

function debounce(fun,wait){
	let timeout
	return function(){
		let context = this
		let args = arguments
		clearTimeout(timeout)
		timeout = setTimeout(function(){
			fun.call(context,...arguments)
		},wait)
	}
}

然後,別忘了返回值:

function debounce(fun,wait){
	let timeout,result
	return function(){
		let context = this
		let args = arguments
		clearTimeout(timeout)
		timeout = setTimeout(function(){
			result = fun.call(context,...arguments)
		},wait)
		return result
	}
}

【總結】:

節流

【原理】:如果持續觸發事件,每隔一段時間就執行一次。

節流的實現主要有兩種方式:時間戳和定時器。

時間戳節流

當觸發事件的時候,取出當前的時間戳,然後減去之前的時間戳,如果大於設置的時間週期,就執行函數,然後更新時間戳;否則不執行。

function(fun,wait){
	let previous = 0
	return function(){
		let now = +new Date()
		let context = this
		let args = arguments
		if(now - previous > wait){
			fun.apply(context,args)
			previous = now
		}
	}
}

定時器節流

當觸發事件時,設置一個定時器,再觸發事件時,就判斷定時器是否存在,存在則不執行。

function(fun,wait){
	let timeout
	let previous
	return function(){
		let context = this
		let args = arguments
		if(!timeout){
			timeout = setTimeout(function(){
				timeout = null
				fun.apply(context,args)
			},wait)
		}
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章