節(jié)流防抖

有些情況下會(huì)產(chǎn)生問(wèn)題:

  • 向后臺(tái)發(fā)送數(shù)據(jù),用戶頻繁觸發(fā),對(duì)服務(wù)器造成壓力
  • 一些瀏覽器事件:window.onresize、window.mousemove等,觸發(fā)的頻率非常高,會(huì)造成瀏覽器性能問(wèn)題。

以上需要用到函數(shù)節(jié)流和防抖;
核心:限制某一個(gè)方法被頻繁觸發(fā),而一個(gè)方法之所以會(huì)被頻繁觸發(fā),大多數(shù)情況下是因?yàn)?DOM 事件的監(jiān)聽(tīng)回調(diào),而這也是函數(shù)節(jié)流以及防抖多數(shù)情況下的應(yīng)用場(chǎng)景。
共同點(diǎn):都為防止事件頻繁觸發(fā);
不同點(diǎn):節(jié)流:?jiǎn)挝粫r(shí)間內(nèi),觸發(fā)第一次事件;防抖:?jiǎn)挝粫r(shí)間內(nèi),觸發(fā)最后一次事件

一、函數(shù)節(jié)流(throttle)

函數(shù)執(zhí)行后,只有大于設(shè)定的執(zhí)行周期后才會(huì)執(zhí)行第二次。
頻繁觸發(fā)的函數(shù),在規(guī)定時(shí)間內(nèi),只讓函數(shù)觸發(fā)的第一次生效,后面不生效。
原理:用時(shí)間戳判斷是否已到回調(diào)該執(zhí)行時(shí)間;記錄上次執(zhí)行的時(shí)間戳,每次觸發(fā)事件執(zhí)行回調(diào),回調(diào)中判斷當(dāng)前時(shí)間戳距離上次執(zhí)行時(shí)間戳的間隔是否已經(jīng)到達(dá)規(guī)定時(shí)間段,如果是,則執(zhí)行,并更新上次執(zhí)行的時(shí)間戳,如此循環(huán);

function throttle(fn, delay) {
    // 記錄上一次函數(shù)觸發(fā)的時(shí)間
    var lastTime = 0;
    return function() {
        // 記錄當(dāng)前函數(shù)觸發(fā)的時(shí)間
        var nowTime = Date.now();
        if (nowTime - lastTime > delay) {
        // 修正this指向問(wèn)題
        fn.call(this);
        // 同步時(shí)間
          lastTime = nowTime;
        }
    }
}
document.onscroll = throttle(function() { 
    console.log('scroll事件被觸發(fā)了' + Date.now()) 
}, 200)
01.png

第二種方法是使用定時(shí)器,比如,當(dāng)scroll事件剛觸發(fā)時(shí),打印一個(gè)hello world ,然后設(shè)置一個(gè)1000ms的定時(shí)器,此后每次觸發(fā)scroll事件,觸發(fā)回調(diào),如果已經(jīng)存在定時(shí)器,則回調(diào)不執(zhí)行方法,直到定時(shí)器觸發(fā),handler被清除,然后重新設(shè)置定時(shí)器。

function throttlePro(delay, action) {
    var tId;
    return function () {
        var context = this;
        var arg = arguments;
        if (tId) return;
        tId = setTimeout(function () {
                    action.apply(context, arg);
                    clearTimeout(tId);
                    // setTimeout 返回一個(gè)整數(shù),clearTimeout 之后,tId還是那個(gè)整數(shù),setInterval同樣如此
                    tId = null;
        }, delay);
    }
}

函數(shù)節(jié)流的應(yīng)用場(chǎng)景

  1. 需要間隔一定時(shí)間觸發(fā)回調(diào)來(lái)控制函數(shù)調(diào)用頻率:
  2. DOM 元素的拖拽功能實(shí)現(xiàn)(mousemove)
  3. 搜索聯(lián)想(keyup)
  4. 計(jì)算鼠標(biāo)移動(dòng)的距離(mousemove)
  5. Canvas 模擬畫(huà)板功能(mousemove)
  6. 射擊游戲的 mousedown/keydown 事件(單位時(shí)間只能發(fā)射一顆子彈)
  7. 監(jiān)聽(tīng)滾動(dòng)事件判斷是否到頁(yè)面底部自動(dòng)加載更多:給 scroll 加了 debounce 后,只有用戶停止?jié)L動(dòng)后,才會(huì)判斷是否到了頁(yè)面底部;如果是 throttle 的話,只要頁(yè)面滾動(dòng)就會(huì)間隔一段時(shí)間判斷一次

二、函數(shù)防抖(debounce)

頻繁觸發(fā)的函數(shù),在規(guī)定時(shí)間內(nèi),只讓最后一次生效,前面的不生效。
原理:第一次調(diào)用函數(shù),創(chuàng)建一個(gè)定時(shí)器,在指定的時(shí)間間隔之后運(yùn)行代碼。當(dāng)?shù)诙握{(diào)用該函數(shù)時(shí),它會(huì)清除前一次的定時(shí)器并設(shè)置另一個(gè)。如果前一個(gè)定時(shí)器已經(jīng)執(zhí)行過(guò)了,這個(gè)操作就沒(méi)有任何意義。然而,如果前一個(gè)定時(shí)器尚未執(zhí)行,其實(shí)就是將其替換為一個(gè)新的定時(shí)器,然后延遲一定時(shí)間再執(zhí)行。

<button id='btn'>按鈕</button>
<script type="text/javascript">
function debounce(fn, delay) {
    // 記錄上一次的延時(shí)器
    var timer = null;
    return function() {
        // 清除上一次延時(shí)器
        clearTimeout(timer)
        timer = setTimeout(function() {
            fn.apply(this)
        }, delay)
    }
}
document.getElementById('btn').onclick = debounce(function() {
    console.log('點(diǎn)擊事件被觸發(fā)' + Date.now())
}, 1000)
</script>

02.png

上面用到了閉包的特性--可以使變量timer的值長(zhǎng)期保存在內(nèi)存中。
函數(shù)防抖的應(yīng)用場(chǎng)景

  1. 對(duì)于連續(xù)的事件響應(yīng)我們只需要執(zhí)行一次回調(diào):
  2. 每次 resize/scroll 觸發(fā)統(tǒng)計(jì)事件
  3. 文本輸入的驗(yàn)證(連續(xù)輸入文字后發(fā)送 AJAX 請(qǐng)求進(jìn)行驗(yàn)證,驗(yàn)證一次就好)

函數(shù)防抖:如果A在10:00:00開(kāi)門走入電梯內(nèi)(觸發(fā)事件),如果后續(xù)沒(méi)有人進(jìn)入電梯,電梯將在5秒鐘之后10:00:05關(guān)門(執(zhí)行事件監(jiān)聽(tīng)器)。這時(shí)如果B在10:00:04走入電梯內(nèi),電梯會(huì)在10:00:09才關(guān)門。
函數(shù)節(jié)流 :如果A在10:00:00開(kāi)門走入電梯內(nèi)(觸發(fā)事件),如果后續(xù)沒(méi)有人進(jìn)入電梯,電梯將在5秒鐘之后10:00:05關(guān)門(執(zhí)行事件監(jiān)聽(tīng)器)。這時(shí)如果B在10:00:04走入電梯內(nèi),電梯同樣是在10:00:05關(guān)門。這個(gè)時(shí)間從第一個(gè)人進(jìn)入電梯開(kāi)始計(jì)時(shí),不管在5秒之內(nèi)進(jìn)來(lái)多少人,電梯都會(huì)在10:00:05關(guān)門。如果一直沒(méi)有人進(jìn)來(lái),則電梯不運(yùn)行。
效果:
函數(shù)防抖是某一段時(shí)間內(nèi)只執(zhí)行一次;
函數(shù)節(jié)流是間隔時(shí)間執(zhí)行,不管事件觸發(fā)有多頻繁,都會(huì)保證在規(guī)定時(shí)間內(nèi)一定會(huì)執(zhí)行一次真正的事件處理函數(shù)。
原理:
防抖是維護(hù)一個(gè)計(jì)時(shí)器,規(guī)定在delay時(shí)間后觸發(fā)函數(shù),但是在delay時(shí)間內(nèi)再次觸發(fā)的話,都會(huì)清除當(dāng)前的 timer 重新計(jì)時(shí)。這樣一來(lái),只有最后一次操作事件才被真正觸發(fā)。
節(jié)流是通過(guò)判斷是否到達(dá)一定時(shí)間來(lái)觸發(fā)函數(shù),若沒(méi)到規(guī)定時(shí)間則使用計(jì)時(shí)器延后,而下一次事件則會(huì)重新設(shè)定計(jì)時(shí)器。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容