一文學(xué)會JavaScript如何手寫防抖節(jié)流
前言
記得當(dāng)初看這篇大佬的文章一杯茶的時間,帶你徹底學(xué)會手寫防抖節(jié)流已經(jīng)對防抖節(jié)流有了個清晰的認(rèn)識,但那個時候由于真的是第一次接觸到防抖節(jié)流,對它的手寫方式還是很迷,卡在著卡了很久。
所以今天打算在那篇文章的基礎(chǔ)上做一些補(bǔ)充,讓小白對防抖節(jié)流的手寫能夠真正掌握。
防抖節(jié)流的概念
- 防抖: n 秒后在執(zhí)行該事件,若在 n 秒內(nèi)被重復(fù)觸發(fā),則重新計時
- 節(jié)流: n 秒內(nèi)只運行一次,若在 n 秒內(nèi)重復(fù)觸發(fā),只有一次生效
一個經(jīng)典的比喻:
想象每天上班大廈底下的電梯。把電梯完成一次運送,類比為一次函數(shù)的執(zhí)行和響應(yīng)
假設(shè)電梯有兩種運行策略 debounce 和 throttle,超時設(shè)定為15秒,不考慮容量限制
電梯第一個人進(jìn)來后,等待15秒。如果過程中又有人進(jìn)來,15秒等待重新計時,直到15秒后開始運送,這是防抖。
電梯第一個人進(jìn)來后,15秒后準(zhǔn)時運送一次,這是節(jié)流。
再舉個不太嚴(yán)謹(jǐn)?shù)谋扔鳎?/p>
防抖:就是王者榮耀里的回城,一段時間內(nèi)被打斷,就又要重新計時。
節(jié)流:就是王者榮耀里英雄的技能,像東皇的大一般都是20s準(zhǔn)時來一次。
手寫
防抖
首先我們先簡單的模擬一個按鈕被點擊的過程。
let addBtn=document.getElementById('add')
function addOne(){
console.log('增加一個')
}
addBtn.addEventListener('click',addOne)加入防抖功能
// debounce.js
let addBtn=document.getElementById('add')
function addOne(){
console.log('增加一個')
}
function debounce(fun,time){
setTimeout(()=>{
fun()
},time)
}
addBtn.addEventListener('click',debounce(addOne,2000))
現(xiàn)在延時的目的是達(dá)到了但是每次點擊都會新增一個新的setTimeout而且并不能達(dá)到我們多次點擊只執(zhí)行一次的效果。
這時候就需要clearTimeout登場了,我們需要在我們點擊了按鈕后也就是debounce執(zhí)行時要先把之前的setTimeout先清除再重新計時。
這時我們會很自然地想到這個寫法:
function debounce(fun, time) {
let timer;
// 如果之前就存在定時器,就要把之前那個定時器刪除
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fun()
}, time)
}但是這樣寫的話拿的到之前定時器的值嗎?
當(dāng)然是拿不到的,因為每次都會重新let timer,所以這個時候我們就要使用閉包(延遲了變量的生命周期)了。
如果你還不太理解閉包,可以參考這篇文章包教包會——作用域鏈+閉包
引入閉包后
function addOne(){
console.log('增加一個')
}
function debounce(fun, time) {
let timer;
return function(){
// 如果之前就存在定時器,就要把之前那個定時器刪除
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fun()
}, time)
}
}
addBtn.addEventListener('click',debounce(addOne,2000))
這里還有一點需要知道的,每執(zhí)行一次addEventListener,debounce因為返回的是一個函數(shù),并結(jié)合addEventListener的特點,會直接執(zhí)行debounce返回的函數(shù),不會出現(xiàn)每次都let timer
- 現(xiàn)在我們的一個防抖功能就完成了,但是這還沒完,如果我們在
addOne()打印this會發(fā)現(xiàn)我們這樣執(zhí)行的this是指向Window的。 - 這當(dāng)然不是我們所希望的,我們需要使用
apply來改變this指向,再者就是我們需要考慮到執(zhí)行函數(shù)的參數(shù),因為不同的函數(shù)肯定會有不同的參數(shù)傳入,對于參數(shù)我們可以使用arguments處理。
function debounce(fun, time) {
let timer;
return function(){
// 如果之前就存在定時器,就要把之前那個定時器刪除
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fun.apply(this, arguments)
}, time)
}
}這樣一個防抖就寫出來了
節(jié)流
- 首先我們先模擬一個觸發(fā)事件。
- 接下來我們封裝一個節(jié)流函數(shù),跟防抖一樣我們也需要利用閉包,順便再加一個參數(shù)接收節(jié)流時間。
// throttle.js
function scrollTest(){
console.log('現(xiàn)在我觸發(fā)了')
}
function throttle(fun,time){
return function(){
fun()
}
}
document.addEventListener('scroll',throttle(scrollTest,3000))
這里用閉包是為了后面獲得上一次的時間
- 因為我們的節(jié)流是在一段時間內(nèi)執(zhí)行一次也就是說如果兩次鼠標(biāo)滾動的時間間隔未到所設(shè)置的時間則不執(zhí)行。
- 那我們可以記錄一下每次滾動的時間戳來進(jìn)行對比。
- 當(dāng)然我們也需要像防抖一樣改變
this指向和接收參數(shù),最后完成后是這樣的。
// throttle.js
...
function throttle(fun,time){
let t1=0 //初始時間
return function(){
let t2=new Date() //當(dāng)前時間
if(t2-t1>time){
fun.apply(this,arguments)
t1=t2
}
}
}應(yīng)用場景
防抖在連續(xù)的事件,只需觸發(fā)一次回調(diào)的場景有:
- 搜索框搜索輸入。只需用戶最后一次輸入完,再發(fā)送請求
- 手機(jī)號、郵箱驗證輸入檢測
- 窗口大小
resize。只需窗口調(diào)整完成后,計算窗口大小。防止重復(fù)渲染。
節(jié)流在間隔一段時間執(zhí)行一次回調(diào)的場景有:
- 滾動加載,加載更多或滾到底部監(jiān)聽
- 搜索框,搜索聯(lián)想功能
到此這篇關(guān)于一文學(xué)會JavaScript如何手寫防抖節(jié)流的文章就介紹到這了,更多相關(guān)JavaScript防抖節(jié)流內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript變量作用域_動力節(jié)點Java學(xué)院整理
這篇文章主要介紹了JavaScript變量作用域,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06
JS實現(xiàn)給json數(shù)組動態(tài)賦值的方法示例
這篇文章主要介紹了JS實現(xiàn)給json數(shù)組動態(tài)賦值的方法,結(jié)合實例形式分析了javascript針對json數(shù)組的遍歷、賦值等常用操作技巧,需要的朋友可以參考下2017-07-07
詳解如何用webpack打包一個網(wǎng)站應(yīng)用項目
本篇文章主要介紹了如何用webpack打包一個網(wǎng)站應(yīng)用,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-07-07
JavaScript 定義function的三種方式小結(jié)
JavaScript中定義function有以下三種方式.2009-10-10
Js md5加密網(wǎng)頁版MD5轉(zhuǎn)換代碼
Js實現(xiàn)網(wǎng)頁上的MD5加密功能,將文字轉(zhuǎn)換為MD5字符,本代碼調(diào)用簡單,你可以新建一個網(wǎng)頁,將此網(wǎng)頁上傳到你的服務(wù)器上,用戶瀏覽網(wǎng)頁,就可實現(xiàn)MD5加密轉(zhuǎn)換功能,用戶可方便查詢?nèi)我蛔址腗D5碼,很不錯的功能2013-03-03

