js 函式防抖和節流

DreamTruth發表於2019-02-22

在前端開發中,我們會遇到一些需要持續觸發的事件或者一些持續的請求,但是我們不希望在事件觸發時過於頻繁的去執行函式。

所以這個時候,就出現了函式防抖和函式節流的解決方案。

下面通過這樣一個場景來說明:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>函式防抖和函式節流</title>
</head>
<body>
    <div id="content" style="height:150px;line-height:150px;text-align:center; color: #ff1e1e;background-color:#3cccc7;font-size:80px;"></div>
    <script type="text/javascript">
        let num = 1;
        let content = document.getElementById('content');

        function count() {
            content.innerHTML = num++;
        };
        content.onmousemove = count;
    </script>
</body>
</html>
複製程式碼

在上述程式碼中,div 元素繫結了 mousemove 事件,當滑鼠在div區域中移動的時候會持續地去觸發該事件導致頻繁執行函式。

可以看到,在沒有通過其它操作的情況下,函式被頻繁地執行導致頁面上資料變化特別快。所以,接下來讓我們來看看防抖和節流是如何去解決這個問題的。

函式防抖(debounce)

函式防抖就是在觸發事件後 N 秒內函式只能執行一次,如果在 N 秒內又觸發了事件,則重新開始計算函式執行時間

函式防抖分為立即執行和非立即執行兩種

  • 函式防抖--非立即執行
//非立即執行
function debounce(fun, delay) {
    let timeout;    //需要一個外部變數,為增強封裝,所以使用閉包
    return function () {
        let _this = this;
        let arg = arguments; //arguments中存著e

        if (timeout) clearTimeout(timeout);

        timeout = setTimeout(()=> {
            fun.apply(_this,arg);
        },delay)
    }
}
複製程式碼

非立即執行版的意思是觸發事件後函式不會立即執行,而是在 n 秒後執行,如果在 n 秒內又觸發了事件,則會重新計算函式執行時間。

我們依舊使用上述繫結 mousemove 事件的例子,通過上面的防抖函式,我們可以這麼使用

content.onmousemove = debounce(count,1000);
複製程式碼

可以看到,在觸發事件後函式 1 秒後才執行,而如果我在觸發事件後的 1 秒內又觸發了事件,則會重新計算函式執行時間。

  • 函式防抖--立即執行
//立即執行
function debounce(fun, delay) {
    let timeout;
    return function () {
        let _this = this;
        let arg = arguments;

        if (timeout) clearTimeout(timeout);

        let applyNow = !timeout;

        timeout = setTimeout(()=> {
            timeout = null;
        },delay)

        if(applyNow) fun.apply(_this,arg);
    }
}
複製程式碼

立即執行版的意思是觸發事件後函式會立即執行,然後 n 秒內不觸發事件才能繼續執行函式的效果。

開發過程中的需求是多樣的,為了適應各種需求,我們可以把上面兩種情況都封裝到一個方法裡,根據實際情況選擇呼叫。

function debounce(fun, delay, immediate) {
    //immediate為true時立即執行,反之為非立即執行
    let timeout;
    return function () {
        let _this = this;
        let arg = arguments;

        if (timeout) clearTimeout(timeout);

        if(immediate){
            //立即執行
            let applyNow = !timeout;

            timeout = setTimeout(()=> {
                timeout = null;
            },delay)

            if(applyNow) fun.apply(_this,arg);
        }else{
            //非立即執行
            timeout = setTimeout(()=>{
                fun.apply(_this,arg);
            },delay)
        }
    }
}
複製程式碼

函式節流(throttle)

函式節流是指在連續觸發事件時函式在 N 秒內只執行一次,節流會稀釋函式的執行頻率

一般有兩種方式可以實現,分別是時間戳版和定時器版。

  • 函式防抖--時間戳版
//時間戳版
function throttle(fun, delay) {
    let previous = 0;
    return function () {
        let _this = this;
        let arg = arguments;

        let now = Date.now();
        console.log(now);
        if(now - previous > delay){
            fun.apply(_this,arg);
            previous = now;
        }

    }
}
複製程式碼
  • 函式防抖--定時器版
//定時器版
function throttle(fun, delay) {
    let timeout;
    return function () {
        let _this = this;
        let arg = arguments;

        if(!timeout){
            timeout = setTimeout(()=>{
                timeout = null;
                fun.apply(_this,arguments);
            },delay)
        }
    }
}
複製程式碼

相關文章