js防抖具體實(shí)現(xiàn)以及詳細(xì)原理步驟說(shuō)明(附實(shí)例)
ps:本文將從一個(gè)案例出發(fā)循序漸進(jìn),在其中你不僅能知道防抖是如何實(shí)現(xiàn)的,還可以學(xué)習(xí)到關(guān)于 this 、apply、arguments 等知識(shí)的具體應(yīng)用。
Why?為啥要有防抖?
因?yàn)橛袝r(shí)候頻繁的事件觸發(fā)是沒(méi)有意義的,不僅影響性能還可能造成卡頓。
為了避免這種情況,我們需要用防抖來(lái)解決這個(gè)問(wèn)題。
What? 啥是防抖?
防抖debounce:簡(jiǎn)單來(lái)說(shuō),防抖的效果就是在一定的時(shí)間間隔內(nèi),多次觸發(fā)只有一次觸發(fā)產(chǎn)生。
How? 防抖咋用?。?/h2>
首先先從一個(gè)實(shí)例入手:
我們?cè)O(shè)置一個(gè)按鈕用來(lái)切換上方文本的顏色,如下:
<h2 id="demo">hello</h2> <button id="btn">點(diǎn)我</button> <script type="text/javascript"> var btn = document.getElementById('btn'); btn.addEventListener('click', changeColor, false); var flag = true function changeColor() { if (flag) { document.getElementById('demo').style.color = 'pink' } else { document.getElementById('demo').style.color = 'blue' } flag = !flag }
如果你想要讓用戶在1秒內(nèi)不能頻繁切換 ,即1秒內(nèi)只能給文本換一種顏色。
那么你可以利用定時(shí)器來(lái)實(shí)現(xiàn),不過(guò)直接寫(xiě)一個(gè)定時(shí)器是不夠的,因?yàn)槟阒荒軐?shí)現(xiàn)多次觸發(fā)延時(shí)執(zhí)行,而不是限制觸發(fā)。
你可以使用一個(gè)變量來(lái)判斷你的每次按下按鈕時(shí)候是否已經(jīng)觸發(fā)過(guò)定時(shí)器了,如果觸發(fā)過(guò)了就將原來(lái)觸發(fā)但還沒(méi)到1秒的定時(shí)器清除,接著重新來(lái)個(gè)1秒的定時(shí)器;如果沒(méi)觸發(fā)過(guò)說(shuō)明你1秒內(nèi)沒(méi)按過(guò),新建一個(gè)1秒的定時(shí)器就行。
這樣就可以保證防抖的效果實(shí)現(xiàn)了,請(qǐng)看代碼:
<h2 id="demo">hello</h2> <button id="btn">點(diǎn)我</button> <script type="text/javascript"> var btn = document.getElementById('btn'); btn.addEventListener('click', debounce(changeColor), false); var flag = true function changeColor() { if (flag) { document.getElementById('demo').style.color = 'pink' } else { document.getElementById('demo').style.color = 'blue' } flag = !flag } function debounce(fn) { let t = null return function () { //如果定時(shí)器存在就清除掉 if (t) { clearTimeout(t) } //不然就創(chuàng)建新的定時(shí)器 t = setTimeout(function() { fn() }, 1000) } } </script>
看完代碼你可能會(huì)有如下疑惑:
1. 怎么這個(gè)debounce函數(shù)里面不能直接寫(xiě)執(zhí)行內(nèi)容嗎?非要return一個(gè)函數(shù)?
解:因?yàn)槟阋呀?jīng)把changeColor函數(shù)當(dāng)成參數(shù)傳給debounce函數(shù)了(看下面的代碼變化),你要在btn上綁定debounce事件就要在debounce里面將改造好的changeColor事件寫(xiě)成一個(gè)回調(diào)函數(shù)。
// btn.addEventListener('click', changeColor, false); btn.addEventListener('click', debounce(changeColor), false);
2. 這個(gè) let t = null; 豈不是會(huì)每次都將 t 置為 null ??這可咋實(shí)現(xiàn)呢?
解:你以為 btn 綁定的是 debounce,所以你認(rèn)為每次觸發(fā)都會(huì)執(zhí)行 t = null。
漏! btn 綁定的是debounce(changeColor)!這個(gè)括號(hào)不可忽視,實(shí)際上每次觸發(fā)click事件執(zhí)行的是它的回調(diào),也就是 return 里面的內(nèi)容,let t = null 只會(huì)在初始化的時(shí)候執(zhí)行一次。
雖然目前已經(jīng)實(shí)現(xiàn)了防抖的效果,但是這么寫(xiě)的話,你會(huì)發(fā)現(xiàn)changeColor函數(shù)是拿不到事件對(duì)象的,也就是說(shuō)它拿不到本該屬于它的 event 。
要想讓它拿回本該屬于它的 event ,你可以這么做:
1.基于當(dāng)前 event 已經(jīng)在 debounce 上了,你可以將 e 當(dāng)參數(shù)傳遞給 debounce 里回調(diào)的函數(shù)
2.再把e傳給回調(diào)函數(shù)中定時(shí)器里的 fn() ,即 fn(e)
function debounce(fn) { let t = null //往這里傳e return function (e) { if (t) { clearTimeout(t) } t = setTimeout(function() { //再帶給fn fn(e) }, 1000) } }
屆時(shí),你就會(huì)發(fā)現(xiàn) changeColor 它拿到了事件對(duì)象!
如果你問(wèn),非要拿這個(gè)e干啥呢??
那么我只能說(shuō),拿到了e可以對(duì)e做一些操作(像是e.target可以更改等等),如果你不對(duì)e做什么不傳給它也沒(méi)關(guān)系,只是這么寫(xiě)可以離一個(gè)完美的防抖函數(shù)更近而已。
但是你不能保證只有一個(gè)參數(shù)需要傳給 changeColor ,所以在傳參的時(shí)候只寫(xiě)一個(gè) e 沒(méi)辦法實(shí)現(xiàn)多個(gè)參數(shù)的傳遞,那么為了更完美一點(diǎn),我們接著來(lái)改改。
說(shuō)到多個(gè)參數(shù)的傳遞你大概會(huì)想到實(shí)參列表 arguments 。沒(méi)錯(cuò)!就是它!
如果你不了解 arguments,請(qǐng)前往:學(xué)arguments去嘍
arguments
對(duì)象是所有(非箭頭)函數(shù)中都可用的局部變量。你可以使用arguments
對(duì)象在函數(shù)中引用函數(shù)的參數(shù)。此對(duì)象包含傳遞給函數(shù)的每個(gè)參數(shù),第一個(gè)參數(shù)在索引0處。
首先,我們?cè)囍?arguments[0] 去代替剛剛傳遞的 e ,你會(huì)發(fā)現(xiàn):
那個(gè)return里的 arguments[0] 根本傳不到定時(shí)器里,那就更別提傳到 changeColor 了。
因?yàn)?strong>每個(gè)函數(shù)里的 arguments 都是自己函數(shù)內(nèi)部的,定時(shí)器里的函數(shù)沒(méi)有 arguments 所以 undefined。
要解決這個(gè)問(wèn)題的話,你可以使用賦值的方式把 arguments 存下來(lái),還可以使用箭頭函數(shù)。
接下來(lái)采用箭頭函數(shù)的方式來(lái)解決:
因?yàn)榧^函數(shù)內(nèi)部沒(méi)有 arguments 對(duì)象,它會(huì)往外找,這樣就可以得到 return 里的 arguments。
function debounce(fn) { let t = null return function () { console.log('我是回調(diào)的arguments',arguments[0]); if (t) { clearTimeout(t) } //這里用箭頭函數(shù) t = setTimeout(() => { fn(arguments[0]) console.log('我是定時(shí)器里的arguments', arguments[0]) // console.log(this) }, 1000) } }
這樣子就實(shí)現(xiàn)了 arguments[0] 的傳遞問(wèn)題,如果是多個(gè)參數(shù)的話可以使用 apply 方法 將整個(gè) arguments 作為參數(shù)傳遞過(guò)去,如下:
function debounce(fn) { let t = null return function () { if (t) { clearTimeout(t) } t = setTimeout(() => { fn.apply(this, arguments) }, 1000) } }
其中,apply方法的第一個(gè)參數(shù)還可以將 changeColor 的 this 由 Window 轉(zhuǎn)為 btn,屬于是一個(gè)一舉兩得的大動(dòng)作了。 (因?yàn)榧^函數(shù)會(huì)往外找this繼承,所以拿到了return里的this再傳給changeColor)
如果你不了解apply,請(qǐng)前往:學(xué)apply去嘍
寫(xiě)到這里,一個(gè) 延遲debounce 就誕生了!
什么是延遲debounce??
顧名思義,在延遲結(jié)束那一刻才觸發(fā)回調(diào)。
如果你覺(jué)得每次按完按鈕還要等等才能改顏色真是太煩了,估計(jì)沒(méi)等到改顏色你就關(guān)閉網(wǎng)頁(yè)了。
前緣debounce 可以解決這個(gè)問(wèn)題?。丛诙〞r(shí)器開(kāi)始的那一刻就觸發(fā)事件)
將代碼再改一改你就可以得到 前緣debounce 啦!
function debounce(fn) { let t = null return function () { // 用firstClick來(lái)記錄每一次定時(shí)器開(kāi)始的第一次按下的動(dòng)作 var firstClick = !t if(t) { clearTimeout(t) } if(firstClick) { fn.apply(this, arguments) } //等待1秒后將 t 置為 null,在這1秒內(nèi)如果再點(diǎn)擊事件就會(huì)去到if(t)的執(zhí)行 t = setTimeout(() => { t = null }, 1000) } }
這個(gè)前緣debounce將會(huì)實(shí)現(xiàn)每次連續(xù)點(diǎn)擊后先響應(yīng)一次事件,再去等1秒。
總結(jié)
到此這篇關(guān)于js防抖具體實(shí)現(xiàn)以及詳細(xì)原理步驟的文章就介紹到這了,更多相關(guān)js防抖實(shí)現(xiàn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JS實(shí)現(xiàn)樹(shù)形結(jié)構(gòu)與數(shù)組結(jié)構(gòu)相互轉(zhuǎn)換并在樹(shù)形結(jié)構(gòu)中查找對(duì)象
這篇文章介紹了JS實(shí)現(xiàn)樹(shù)形結(jié)構(gòu)與數(shù)組結(jié)構(gòu)相互轉(zhuǎn)換并在樹(shù)形結(jié)構(gòu)中查找對(duì)象的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06???????基于el-table和el-pagination實(shí)現(xiàn)數(shù)據(jù)的分頁(yè)效果流程詳解
本文主要介紹了???????基于el-table和el-pagination實(shí)現(xiàn)數(shù)據(jù)的分頁(yè)效果,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-11-11鼠標(biāo)拖拽移動(dòng)子窗體的JS實(shí)現(xiàn)
這篇文章主要介紹了鼠標(biāo)拖拽移動(dòng)子窗體的JS實(shí)現(xiàn),需要的朋友可以參考下2014-02-02詳解JavaScript中Arguments對(duì)象用途
本文主要介紹了詳解JavaScript中Arguments對(duì)象用途,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08JS實(shí)現(xiàn)1000以內(nèi)被3或5整除的數(shù)字之和
今天在技術(shù)群里看到一道這樣的提:求1000以內(nèi)被3或5整除的數(shù)字之和。小編把我的解決辦法分享到腳本之家平臺(tái),供大家參考2016-02-02JS+CSS實(shí)現(xiàn)隨機(jī)點(diǎn)名(實(shí)例代碼)
本文通過(guò)js html和cass代碼實(shí)現(xiàn)了隨機(jī)點(diǎn)名效果,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2019-11-11JavaScript避免內(nèi)存泄露及內(nèi)存管理技巧
這篇文章主要介紹了JavaScript避免內(nèi)存泄露及內(nèi)存管理技巧,主要包括了delete應(yīng)用、閉包、DOM泄露、Timers計(jì)(定)時(shí)器泄露等等,需要的朋友可以參考下2014-09-09