手動實現JS防抖

原文地址: https://www.jeremyjone.com/704/,轉載請註明。


什麼是防抖

事件響應函數在一段時間後才執行,如果在這段時間內再次調用,則重新計算執行時間;當預定的時間內沒有再次調用該函數,則執行該函數。

防抖做什麼

防止某些函數的頻繁調用,保證頁面的穩定流暢和數據準確性。

一個小的例子

使用 underscore 的防抖功能來測試一下效果。

中文網址

在頁面中直接導入cdn即可。

https://cdn.bootcss.com/underscore.js/1.9.1/underscore.js

未防抖時的樣子

將下面內容粘貼到一個HTML的body標籤中。

<div id="container" style="width:100%;height:200px;line-height:200px;text-align:center;color:#fff;background-color:#444;font-size:30px;"></div>
<script>
    let count = 0;
    let container = document.querySelector("#container");

    // 此處爲高頻調用函數
    function doSomething() {
        container.innerHTML = count++;
    }

    container.onmousemove = doSomething;
</script>

這段代碼會生成一個灰色框,只要鼠標在其內部移動,就會調用 doSomething 函數,導致數字不斷增長。

使用防抖

同樣地,將下面內容粘貼到一個新的HTML的body標籤中。

<div id="container" style="width:100%;height:200px;line-height:200px;text-align:center;color:#fff;background-color:#444;font-size:30px;"></div>
<script src="https://cdn.bootcss.com/underscore.js/1.9.1/underscore.js"></script>
<script>
    let count = 0;
    let container = document.querySelector("#container");

    // 此處爲高頻調用函數
    function doSomething() {
        container.innerHTML = count++;
    }

    container.onmousemove = _.debounce(doSomething, 300);
</script>

這裏導入了 underscore 庫,這個庫會導出一個 _ 的對象,它包含一個 debounce 方法,該方法的作用就是防抖。第一個參數是函數原型,第二個參數是響應時間,另外還可以設置是否立即執行,如果是,傳入 true。這裏我們設置300ms後響應。

這次運行後,可以發現當鼠標移動時,不再一味地增加,而是當鼠標靜止或移出後一段時間(300ms)纔會響應。

手動實現防抖函數

如果僅僅使用防抖函數,導入一個庫是得不償失的,因爲防抖函數本身並不大,所以可以手寫一個。

function debounce(func, wait, immediate) {
    let timeout, result;

    var debounced = function() {
        // 獲取上下文,關聯this的指向
        let context = this;
        // 獲取所有參數
        const args = arguments;
        // 每次防抖都清除定時器,然後設置一個新的定時器。
        if (timeout) clearTimeout(timeout);

        // 區別是否立即執行
        if (immediate) {
            // 如果立即執行,需要一個變量控制重複執行。這裏利用timeout取反,可以控制效果
            let callNow = !timeout;
            timeout = setTimeout(() => {
                timeout = null;
            }, wait);

            // 立即執行
            if (callNow) result = func.apply(context, args);
        } else {
            // 不立即執行,則正常定時器等待即可
            timeout = setTimeout(function() {
                result = func.apply(context, args);
            }, wait);
        }

        // 返回調用函數的結果
        return result;
    }

    // 設置一個清除函數,可以手動控制取消防抖函數的執行。
    debounced.cancel = function() {
        clearTimeout(timeout);
        timeout = null;
    }

    return debounced;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章