溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

JavaScript的防抖和節(jié)流怎么實現(xiàn)

發(fā)布時間:2022-03-11 16:29:28 來源:億速云 閱讀:152 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹了JavaScript的防抖和節(jié)流怎么實現(xiàn)的相關(guān)知識,內(nèi)容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇JavaScript的防抖和節(jié)流怎么實現(xiàn)文章都會有所收獲,下面我們一起來看看吧。

1. 前言

首先來舉個例子。百度首頁的百度輸入框,用戶輸入的時候,每次輸入的信息,我們都能看到百度服務(wù)器返回給我們的聯(lián)想關(guān)鍵字。我們每改動一個字,它就換一次聯(lián)想詞,這是我們?nèi)庋勰芸吹降乃俣?,實際上如果不加以處理,可能已經(jīng)上服務(wù)器發(fā)起了好幾十次的同一個關(guān)鍵字聯(lián)想請求了,具體速度依賴于不同的pc等機器上的運行速度不同。那么,剛剛也談到,對于同一個關(guān)鍵字,請求這么多次,也許想給用戶呈現(xiàn)的就一次,剩下的請求都是浪費的,并且如果成千上萬甚至上億的用戶同時請求,對服務(wù)器的負(fù)擔(dān)是巨大的

防抖節(jié)流解決的問題:

在連續(xù)觸發(fā)的事件中,事件處理函數(shù)的頻繁調(diào)用會加重瀏覽器或服務(wù)器的性能負(fù)擔(dān)導(dǎo)致用戶體驗糟糕,有哪些連續(xù)觸發(fā)的事件呢 ?

比如,瀏覽器滾動條的滾動事件、瀏覽器窗口調(diào)節(jié)的resize事件、輸入框內(nèi)容校驗以及在移動端的touchmove事件等。

所以,我們將采用防抖函數(shù)(debounce )和節(jié)流函數(shù)(throttle)來限制事件處理函數(shù)的調(diào)用頻率。

總的來說:防抖函數(shù)(debounce )和節(jié)流函數(shù)(throttle)是在時間軸上控制函數(shù)的執(zhí)行次數(shù)。

2. 函數(shù)防抖(debounce)

延遲防抖

延遲防抖(debounce): 在事件被觸發(fā)n秒后再執(zhí)行回調(diào),如果在這n秒內(nèi)又被觸發(fā),則重新計時。

生活中的實例: 如果有人進電梯(觸發(fā)事件),那電梯將在10秒鐘后出發(fā)(執(zhí)行事件監(jiān)聽器),這時如果又有人進電梯了(在10秒內(nèi)再次觸發(fā)該事件),我們又得等10秒再出發(fā)(重新計時)。

當(dāng)持續(xù)觸發(fā)事件時,一定時間段內(nèi)沒有再觸發(fā)事件,事件處理函數(shù)才會執(zhí)行一次。

如果設(shè)定的時間到來之前,又一次觸發(fā)了事件,就重新開始延時。

如下圖,持續(xù)觸發(fā)click事件時,并不執(zhí)行handle函數(shù),當(dāng)1000毫秒內(nèi)沒有觸發(fā)click事件時,才會延時觸發(fā)click事件。

JavaScript的防抖和節(jié)流怎么實現(xiàn)

前緣防抖

執(zhí)行動作在前,然后設(shè)定周期,周期內(nèi)有事件被觸發(fā),不執(zhí)行動作,且周期重新設(shè)定。

為什么要這樣呢?

試想第一種延遲debounce,我們本來想對用戶輸入的關(guān)鍵字,發(fā)起請求聯(lián)想的頻率降低,但是如果用戶在我們設(shè)定的時間中,一直輸入,導(dǎo)致的就是,用戶一直看不到關(guān)鍵字,我們倒不如第一次輸入的時候就發(fā)起一個請求,服務(wù)器返回結(jié)果,呈現(xiàn)給用戶,然后后續(xù)用戶的鍵入結(jié)束在繼續(xù)請求)。

JavaScript的防抖和節(jié)流怎么實現(xiàn)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>防抖</title>
</head>
<body>
    <button id="debounce1">點我防抖吶!</button>
<script>
    function handle() {
      console.log("防抖成功!");
    }
    window.onload = function() {
        // 1. 獲取按鈕,并綁定事件
        var myDebounce = document.getElementById('debounce1');
        myDebounce.addEventListener('click',debounce(handle,1000,true));
    }
    // 防抖函數(shù)
    function debounce(fn,wait, immediate ){
        //2. 設(shè)置時間戳,使用setTimeout讓返回函數(shù)延遲執(zhí)行
        let timer, result;
        return function(...args){
            // 3. timer存在,將定時器中的函數(shù)清除
            if(timer) clearTimeout(timer);
            // 4.1 立即執(zhí)行返回函數(shù)
            if(immediate){
                if(!timer){
                    result = fn.apply(this,args);
                }
                timer = setTimeout(() => {
                    timer = null;
                },wait);
            }else{  // 4.2 非立即執(zhí)行返回函數(shù)
                timer = setTimeout(() => {
                    fn.apply(this,args);
                },wait);
            }
        }
        // 5. 立即執(zhí)行時返回函數(shù)的返回值
        return result;
    }
</script>
</body>
</html>

實現(xiàn)效果:

JavaScript的防抖和節(jié)流怎么實現(xiàn)

原理解析:

  • 防抖函數(shù)作用,對傳入的函數(shù)進行延時包裝后返回

  • setTimeout在前一次未執(zhí)行完前,第二次次觸發(fā)將會覆蓋掉前面的定時器,執(zhí)行第二次的功能

  • 前一次由于異步加延時還未執(zhí)行完,使用clearTimeout清除前面定時器,取消上次的fn功能

  • 為保持fn內(nèi)部this的指向,使用apply改變this指向

  • fn傳入為函數(shù),不是函數(shù)的調(diào)用

防抖函數(shù)實現(xiàn)總結(jié)

function debounce(fn,wait, immediate ){
    //2. 設(shè)置時間戳,使用setTimeout讓返回函數(shù)延遲執(zhí)行
    let timer, result;
    return function(...args){
        // 3. timer存在,將定時器中的函數(shù)清除
        if(timer) clearTimeout(timer);
        // 4.1 立即執(zhí)行返回函數(shù)
        if(immediate){
            if(!timer){
                result = fn.apply(this,args);
            }
            timer = setTimeout(() => {
                timer = null;
            },wait);
        }else{  // 4.2 非立即執(zhí)行返回函數(shù)
            timer = setTimeout(() => {
                fn.apply(this,args);
            },wait);
        }
    }
    // 5. 立即執(zhí)行時返回函數(shù)的返回值
    return result;
}

3. 函數(shù)節(jié)流(throttling)

throttling,節(jié)流的策略是,固定周期內(nèi),只執(zhí)行一次動作,若有新事件觸發(fā),不執(zhí)行。周期結(jié)束后,又有事件觸發(fā),開始新的周期。 節(jié)流策略也分前緣和延遲兩種。

與debounce類似,延遲是指 周期結(jié)束后執(zhí)行動作,前緣是指執(zhí)行動作后再開始周期。

  • 節(jié)流會稀釋函數(shù)的執(zhí)行頻率

  • 在持續(xù)觸發(fā)事件的過程中,函數(shù)會立即執(zhí)行,并且每n秒執(zhí)行一次

生活中的實例: 我們知道目前的一種說法是當(dāng) 1 秒內(nèi)連續(xù)播放 24 張以上的圖片時,在人眼的視覺中就會形成一個連貫的動畫,所以在電影的播放(以前是,現(xiàn)在不知道)中基本是以每秒 24 張的速度播放的,為什么不 100 張或更多是因為 24 張就可以滿足人類視覺需求的時候,100 張就會顯得很浪費資源

延遲節(jié)流

JavaScript的防抖和節(jié)流怎么實現(xiàn)

前緣節(jié)流

JavaScript的防抖和節(jié)流怎么實現(xiàn)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>節(jié)流</title>
</head>
<body>
    <button id="debounce1">點我節(jié)流吶!</button>
<script>
    function handle() {
      console.log("節(jié)流成功!");
    }
    window.onload = function() {
        var myDebounce = document.getElementById('debounce1');
        myDebounce.addEventListener('click',throttling(handle,1000,false));
    }
    // 節(jié)流函數(shù)
    function throttling(fn,wait,immediate){
        let timer;
        return function(...args) {
            if(!timer){
                if(immediate){
                    fn.apply(this,args);
                }
                timer = setTimeout(() => {
                    if(!immediate) {
                        fn.apply(this,args);
                    }
                    timer = null;
                },wait);
            }
        }
    }
</script>
</body>
</html>

實現(xiàn)效果:

JavaScript的防抖和節(jié)流怎么實現(xiàn)

節(jié)流函數(shù)實現(xiàn)總結(jié)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>節(jié)流</title>
</head>
<body>
    <button id="debounce1">點我節(jié)流吶!</button>
<script>
    function handle() {
      console.log("節(jié)流成功!");
    }
    window.onload = function() {
        var myDebounce = document.getElementById('debounce1');
        myDebounce.addEventListener('click',throttling(handle,1000,false));
    }
    // 節(jié)流函數(shù)
    function throttling(fn,wait,immediate){
        let timer;
        return function(...args) {
            if(!timer){
                if(immediate){
                    fn.apply(this,args);
                }
                timer = setTimeout(() => {
                    if(!immediate) {
                        fn.apply(this,args);
                    }
                    timer = null;
                },wait);
            }
        }
    }
</script>
</body>
</html>

4. 兩者區(qū)別

函數(shù)節(jié)流不管事件觸發(fā)有多頻繁,都會保證在規(guī)定時間內(nèi)一定會執(zhí)行一次真正的事件處理函數(shù)。

函數(shù)防抖只是在最后一次事件后才觸發(fā)一次函數(shù)。

比如在頁面的無限加載場景下,我們需要用戶在滾動頁面時,每隔一段時間發(fā)一次 Ajax 請求,而不是在用戶停下滾動頁面操作時才去請求數(shù)據(jù)。這樣的場景,就適合用節(jié)流技術(shù)來實現(xiàn)。

5. 應(yīng)用場景

對于函數(shù)防抖,有以下幾種應(yīng)用場景:

  • 給按鈕加函數(shù)防抖防止表單多次提交。

  • 對于輸入框連續(xù)輸入進行AJAX驗證時,用函數(shù)防抖能有效減少請求次數(shù)。

  • 判斷scroll是否滑到底部,滾動事件+函數(shù)防抖

總的來說,適合多次事件一次響應(yīng)的情況

對于函數(shù)節(jié)流,有如下幾個場景:

  • 游戲中的刷新率

  • DOM元素拖拽

  • Canvas畫筆功能

總的來說,適合大量事件按時間做平均分配觸發(fā)。

關(guān)于“JavaScript的防抖和節(jié)流怎么實現(xiàn)”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對“JavaScript的防抖和節(jié)流怎么實現(xiàn)”知識都有一定的了解,大家如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI