函數(shù)節(jié)流和防抖

前言

瀏覽器中某些計(jì)算和處理要比其他的昂貴很多。例如DOM操作比DOM交互需要更多的時(shí)間和cpu時(shí)間,為了提升性能,減少DOM操作,于是,函數(shù)節(jié)流防抖和應(yīng)運(yùn)而生,其函數(shù)節(jié)流的基本思想是指,某些代碼不可以在沒(méi)有間斷的情況下連續(xù)重復(fù)執(zhí)行。函數(shù)防抖的基本思想是指,一個(gè)頻繁觸發(fā)的事情只讓最后一次執(zhí)行。下面就讓我們來(lái)認(rèn)真了解下這經(jīng)常使用的函數(shù)節(jié)流和防抖。

函數(shù)節(jié)流

函數(shù)節(jié)流:一個(gè)函數(shù)執(zhí)行一次后,只有大于設(shè)定的執(zhí)行周期后才會(huì)執(zhí)行第二次。有個(gè)需要頻繁觸發(fā)函數(shù),出于優(yōu)化性能角度,在規(guī)定時(shí)間內(nèi),只讓函數(shù)觸發(fā)的第一次生效,后面不生效。

下面主要介紹時(shí)間戳和定時(shí)器兩種方式來(lái)實(shí)現(xiàn)節(jié)流函數(shù)。

  • 時(shí)間戳實(shí)現(xiàn)函數(shù)節(jié)流

根據(jù)函數(shù)節(jié)流的原理,我們也可以不依賴 setTimeout實(shí)現(xiàn)函數(shù)節(jié)流。

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;
        }
    }
}

測(cè)試代碼:

// test
function testThrottle(e, content) {
    console.log(e, content);
}
var testThrottleFn = throttle(testThrottle, 1000); // 節(jié)流函數(shù)
document.onmousemove = function (e) {
    testThrottleFn(e, 'throttle'); // 給節(jié)流函數(shù)傳參
}

其實(shí)現(xiàn)原理,通過(guò)比對(duì)上一次執(zhí)行時(shí)間與本次執(zhí)行時(shí)間的時(shí)間差與間隔時(shí)間的大小關(guān)系,來(lái)判斷是否執(zhí)行函數(shù)。若時(shí)間差大于間隔時(shí)間,則立刻執(zhí)行一次函數(shù)。并更新上一次執(zhí)行時(shí)間。

  • 定時(shí)器實(shí)現(xiàn)函數(shù)節(jié)流
function throttle(fn, delay) {
    var timer;
    return function () {
        var _this = this;
        var args = arguments;
        if (timer) {
            return;
        }
        timer = setTimeout(function () {
            fn.apply(_this, args);
            timer = null; // 在delay后執(zhí)行完fn之后清空timer,此時(shí)timer為假,
            throttle觸發(fā)可以進(jìn)入計(jì)時(shí)器
        }, delay)
    }
}

測(cè)試代碼:

function testThrottle(e, content) {
    console.log(e, content);
}
var testThrottleFn = throttle(testThrottle, 2000); // 節(jié)流函數(shù)
document.onmousemove = function (e) {
    testThrottleFn(e, 'throttle'); // 給節(jié)流函數(shù)傳參
}

上面例子中,如果我們一直在瀏覽器中移動(dòng)鼠標(biāo)(比如10s),則在這10s內(nèi)會(huì)每隔2s執(zhí)行一次testThrottle,這就是函數(shù)節(jié)流。

函數(shù)節(jié)流的目的,是為了限制函數(shù)一段時(shí)間內(nèi)只能執(zhí)行一次。因此,定時(shí)器實(shí)現(xiàn)節(jié)流函數(shù)通過(guò)使用定時(shí)任務(wù),延時(shí)方法執(zhí)行。在延時(shí)的時(shí)間內(nèi),方法若被觸發(fā),則直接退出方法。從而,實(shí)現(xiàn)函數(shù)一段時(shí)間內(nèi)只執(zhí)行一次。

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

需要間隔一定時(shí)間觸發(fā)回調(diào)來(lái)控制函數(shù)調(diào)用頻率

  • DOM 元素的拖拽功能實(shí)現(xiàn)(mousemove)
  • 搜索聯(lián)想(keyup)
  • 計(jì)算鼠標(biāo)移動(dòng)的距離(mousemove)
  • Canvas 模擬畫板功能(mousemove)
  • 滾動(dòng)加載,加載更多或滾到底部監(jiān)聽(tīng)
  • 谷歌搜索框,搜索聯(lián)想功能
  • 高頻點(diǎn)擊提交,表單重復(fù)提交

函數(shù)防抖

防抖函數(shù):一個(gè)需要頻繁觸發(fā)的函數(shù),在規(guī)定時(shí)間內(nèi),只讓最后一次生效,前面的不生效。

function debounce(fn, delay) {
    var timer = null; 
    return function () {
        var _this = this; // 取debounce執(zhí)行作用域的this
        var args = arguments;
        if (timer) {
            clearTimeout(timer);
        }
        timer = setTimeout(function () {
            fn.apply(_this, args); // 用apply指向調(diào)用debounce的對(duì)象,
            相當(dāng)于_this.fn(args);
        }, delay);
    };
}

測(cè)試代碼:

function testDebounce(e, content) {
    console.log(e, content);
}
var testDebounceFn = debounce(testDebounce, 1000); // 防抖函數(shù)
document.onmousemove = function (e) {
    testDebounceFn(e, 'debounce'); // 給防抖函數(shù)傳參
}

上面例子中的debounce就是防抖函數(shù),在document中鼠標(biāo)移動(dòng)的時(shí)候,會(huì)在onmousemove最后觸發(fā)的1s后執(zhí)行回調(diào)函數(shù)testDebounce;如果我們一直在瀏覽器中移動(dòng)鼠標(biāo)(比如10s),會(huì)發(fā)現(xiàn)會(huì)在10+1s后才會(huì)執(zhí)行testDebounce函數(shù)(因?yàn)?code>clearTimeout(timer)),這個(gè)就是函數(shù)防抖。

函數(shù)防抖的應(yīng)用場(chǎng)景

對(duì)于連續(xù)的事件響應(yīng)我們只需要執(zhí)行一次回調(diào):

  • 每次 resize/scroll 觸發(fā)統(tǒng)計(jì)事件
  • 文本輸入的驗(yàn)證(連續(xù)輸入文字后發(fā)送 AJAX 請(qǐng)求進(jìn)行驗(yàn)證,驗(yàn)證一次就好)
  • 搜索框搜索輸入。只需用戶最后一次輸入完,再發(fā)送請(qǐng)求
  • 手機(jī)號(hào)、郵箱驗(yàn)證輸入檢測(cè)
  • 窗口大小Resize。只需窗口調(diào)整完成后,計(jì)算窗口大小。防止重復(fù)渲染。

總結(jié)

函數(shù)節(jié)流和函數(shù)去抖的核心其實(shí)就是限制某一個(gè)方法被頻繁觸發(fā),其目的都是,降低回調(diào)執(zhí)行頻率,節(jié)省計(jì)算資源,提高瀏覽器的性能。

更多優(yōu)質(zhì)文章可以訪問(wèn)GitHub博客,歡迎帥哥美女前來(lái)Star?。?!

?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 無(wú)意間讀了馮侖的一本書《野蠻生長(zhǎng)》,就一個(gè)感悟:牛人成功總是有道理的,不僅僅是運(yùn)氣,更多的是努力和拼命。 這本書怎...
    輝輝隨筆閱讀 227評(píng)論 0 0
  • 以前一直是恐懼寫東西的,因?yàn)橛X(jué)得自己沒(méi)有寫作經(jīng)驗(yàn),覺(jué)得自己沒(méi)有邏輯,寫的亂七八糟的,所以即使自己有很多的話想...
    信念_1dfd閱讀 502評(píng)論 0 2
  • 今年的春分時(shí)間在3月20日,雖然春分已經(jīng)快要過(guò)去了,現(xiàn)在卻是反饋上一個(gè)季節(jié):春分--四季占卜的好時(shí)機(jī)。 不做...
    海鮮Bethany閱讀 688評(píng)論 0 0
  • 微演講打卡練習(xí)第385天,每天一分鐘,成為最美好的自己 下午有省里的領(lǐng)導(dǎo)來(lái)社區(qū)檢查綜治維穩(wěn)工作,安排我給...
    古月無(wú)語(yǔ)閱讀 178評(píng)論 0 2
  • 夕陽(yáng)與天藍(lán)交輝在一起, 似是白天不愿夜晚的來(lái)臨, 可寒冷卻吹了又吹。 燈光終于亮起, 黑幕也在車如馬龍中愈顯深邃,...
    在下王某某閱讀 357評(píng)論 0 0

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