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