一文學會JavaScript如何手寫防抖節(jié)流
前言
記得當初看這篇大佬的文章一杯茶的時間,帶你徹底學會手寫防抖節(jié)流已經(jīng)對防抖節(jié)流有了個清晰的認識,但那個時候由于真的是第一次接觸到防抖節(jié)流,對它的手寫方式還是很迷,卡在著卡了很久。
所以今天打算在那篇文章的基礎(chǔ)上做一些補充,讓小白對防抖節(jié)流的手寫能夠真正掌握。
防抖節(jié)流的概念
- 防抖: n 秒后在執(zhí)行該事件,若在 n 秒內(nèi)被重復觸發(fā),則重新計時
- 節(jié)流: n 秒內(nèi)只運行一次,若在 n 秒內(nèi)重復觸發(fā),只有一次生效
一個經(jīng)典的比喻:
想象每天上班大廈底下的電梯。把電梯完成一次運送,類比為一次函數(shù)的執(zhí)行和響應(yīng)
假設(shè)電梯有兩種運行策略 debounce
和 throttle
,超時設(shè)定為15秒,不考慮容量限制
電梯第一個人進來后,等待15秒。如果過程中又有人進來,15秒等待重新計時,直到15秒后開始運送,這是防抖。
電梯第一個人進來后,15秒后準時運送一次,這是節(jié)流。
再舉個不太嚴謹?shù)谋扔鳎?/p>
防抖:就是王者榮耀里的回城,一段時間內(nèi)被打斷,就又要重新計時。
節(jié)流:就是王者榮耀里英雄的技能,像東皇的大一般都是20s準時來一次。
手寫
防抖
首先我們先簡單的模擬一個按鈕被點擊的過程。
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)在延時的目的是達到了但是每次點擊都會新增一個新的setTimeout
而且并不能達到我們多次點擊只執(zhí)行一次的效果。
這時候就需要clearTimeout
登場了,我們需要在我們點擊了按鈕后也就是debounce
執(zhí)行時要先把之前的setTimeout
先清除再重新計時。
這時我們會很自然地想到這個寫法:
function debounce(fun, time) { let timer; // 如果之前就存在定時器,就要把之前那個定時器刪除 if (timer) { clearTimeout(timer) } timer = setTimeout(() => { fun() }, time) }
但是這樣寫的話拿的到之前定時器的值嗎?
當然是拿不到的,因為每次都會重新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
的。 - 這當然不是我們所希望的,我們需要使用
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í)行一次也就是說如果兩次鼠標滾動的時間間隔未到所設(shè)置的時間則不執(zhí)行。
- 那我們可以記錄一下每次滾動的時間戳來進行對比。
- 當然我們也需要像防抖一樣改變
this
指向和接收參數(shù),最后完成后是這樣的。
// throttle.js ... function throttle(fun,time){ let t1=0 //初始時間 return function(){ let t2=new Date() //當前時間 if(t2-t1>time){ fun.apply(this,arguments) t1=t2 } } }
應(yīng)用場景
防抖在連續(xù)的事件,只需觸發(fā)一次回調(diào)的場景有:
- 搜索框搜索輸入。只需用戶最后一次輸入完,再發(fā)送請求
- 手機號、郵箱驗證輸入檢測
- 窗口大小
resize
。只需窗口調(diào)整完成后,計算窗口大小。防止重復渲染。
節(jié)流在間隔一段時間執(zhí)行一次回調(diào)的場景有:
- 滾動加載,加載更多或滾到底部監(jiān)聽
- 搜索框,搜索聯(lián)想功能
到此這篇關(guān)于一文學會JavaScript如何手寫防抖節(jié)流的文章就介紹到這了,更多相關(guān)JavaScript防抖節(jié)流內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript變量作用域_動力節(jié)點Java學院整理
這篇文章主要介紹了JavaScript變量作用域,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06JS實現(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-07JavaScript 定義function的三種方式小結(jié)
JavaScript中定義function有以下三種方式.2009-10-10Js 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