防抖與節流
爲什麼使用防抖節流?
在前端開發中有一部分的用戶行爲會頻繁的觸發事件執行,而對於DOM操作、資源加載等耗費性能的處理,很可能導致界面卡頓,甚至瀏覽器的崩潰。函數節流(throttle)和函數防抖(debounce)就是爲了解決類似需求應運而生的。
防抖(debounce)
函數防抖就是在函數需要頻繁觸發情況時,只有足夠空閒的時間,才執行一次。好像公交司機會等人都上車後才關門一樣。他不會上來一個人就觸發一次關門,而是等人陸續上來後等待一會再觸發關門
場景:(當時間頻繁觸發後停止一段時間執行)
- 實時搜索(keyup)
- 拖拽( mousemove )
- 手機號、郵箱驗證輸入檢測
- 窗口大小Resize。只需窗口調整完成後,計算窗口大小。防止重複渲染。
我們以百度搜索框在輸入完成後停頓一段時間便會進ajax請求爲例子,我們實現輸入框停止輸入後再打印
//使用高階函數,利用閉包封裝
<!-- <script src="../../plugin/helpers.js"></script> -->
let oInp = document.getElementById('inp')
let timer = null
const debounce = (handler, delay) => {
let timer = null //利用閉包保存同一個timer
return () => {
let _self = this //取debounce執行作用域的this
let _arg = arguments //利用閉包保存參數數組
clearTimeout(timer) //不斷的執行函數不斷的清除定時器
timer = setTimeout(() => {
handler.apply(_self, _arg) //用apply指向調用debounce的對象,相當於_this.handler(args);
}, delay)
}
}
let shouValue = (e) => {
console.log(e,this.value)
}
oInp.oninput = debounce(shouValue, 1000);
節流(throttle)
函數節流就是預定一個函數只有在大於等於執行週期時才執行,週期內調用不執行。好像水滴攢到一定重量纔會落下一樣。
場景: CON(不管時間觸發的多麼頻繁,至少要每隔一段時間執行而不是等事件觸發完了等一段時間執行)
窗口調整(resize)
頁面滾動( scroll)
搶購瘋狂點擊( mousedown)
目前我有兩種思路:
- 使用定時器,定時一秒鐘之後去執行,但是在這1s中不停的調用,不讓他的定時器清零重新計時,不會影響當前的結果,還是那1s繼續等,等1秒時觸發(會出現停止操作還是會觸發)
//保證一個時間段內執行一次
const throttle = (handler, time) => {
let timer
return () => {
if (timer) {
return //判斷如果有計時器不清零直接返回啥也不做
}
let _self = this //取throttle 執行作用域的this
let _arg = arguments //利用閉包保存參數數組
timer = setTimeout(() => {
handle.apply(_self, _arg)
timer = null
}, time)
}
}
//觸發事件
window.onresize = () => handle()
//處理函數
let test = () =>console.log("a")
//調用throttle函數傳參
let handle = myPlugin.throttle(test, 2000)
- 使用時間戳,先立即執行,只不過在下一次執行要等一段時間
const throttle = (handler, time) => {
let t
return () => {
let _self = this //取throttle 執行作用域的this
let _arg = arguments //利用閉包保存參數數組
if (!t || Date.now() - t >= time ) {
handler.apply(_self , _arg );
t = Date.now(); //得到的當前時間戳
}
}
}
window.onresize = () => handle()
let test = () =>console.log("a")
let handle = myPlugin.throttle(test, 2000)
判斷兩種方式差異,我們只需要判斷是否需要立即執行,我們就可以把節流的兩種合併在一起
//保證一段時間只執行一次
constthrottle = (handler, time, immediately) => {
if (immediately === undefined) {
immediately = true //判斷需要先立即執行
}
if (immediately) {
let t
return () => {
let _self = this //取throttle 執行作用域的this
let _arg = arguments //利用閉包保存參數數組
if (!t || Date.now() - t >= time) {
handler.apply(_self, _arg);
t = Date.now(); //得到的當前時間戳
}
}
}
else {
let timer
return () => {
if (timer) {
return //判斷如果有計時器不清零直接返回啥也不做
}
let _self = this //取throttle 執行作用域的this
let _arg = arguments //利用閉包保存參數數組
timer = setTimeout(() => {
handle.apply(_self, _arg)
timer = null
}, time)
}
}
}
window.onresize = () => handle()
let test = () => console.log("a")
let handle = myPlugin.throttle(test, 2000,true)
tip:>(免費獲取最新完整前端課程關注vx公衆號:前端拓路者coder,回覆:資料
如果這個文章對你有用的話,歡迎點贊轉發關注,讓更多的小夥伴看到呀,畢竟分享是一個程序員最基本的美德!!!
如果有不對的請大佬指教)