天天看點

JavaScript 函數防抖

<!DOCTYPE html>
<html lang="zh-cmn-Hans"><head>
    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge, chrome=1">
    <title>debounce</title>
    <style>
        #container{
            width: 100%; height: 200px; line-height: 200px; text-align: center; color: #fff; background-color: #444; font-size: 30px;
        }
    </style>
</head><body>
    <div id="container"></div>
    <script src="debounce.js"></script>
</body></html>      

​debounce.js​

​ 檔案的代碼如下:

var count = 1;
var container = document.getElementById('container');function getUserAction() {
    container.innerHTML = count++;
};container.onmousemove = getUserAction;      

觸發事件,但是我一定在事件觸發 n 秒後才執行,如果你在一個事件觸發的 n 秒内又觸發了這個事件,那我就以新的事件的時間為準,n 秒後才執行,總之,就是要等你觸發完事件 n 秒内不再觸發事件,

// 第一版
function debounce(func, wait) {
    var timeout;
    return function () {
        clearTimeout(timeout)
        timeout = setTimeout(func, wait);
    }
}      

container.onmousemove = debounce(getUserAction, 1000);

如果我們在 ​

​getUserAction​

​​ 函數中 ​

​console.log(this)​

​​,在不使用 ​

​debounce​

​​ 函數的時候,​

​this​

​ 的值為:

<div id="container"></div>

但是如果使用我們的 debounce 函數,this 就會指向 Window 對象!

是以我們需要将 this 指向正确的對象。

// 第二版
function debounce(func, wait) {
    var timeout;    return function () {
        var context = this;        clearTimeout(timeout)
        timeout = setTimeout(function(){
            func.apply(context)
        }, wait);
    }
}      

event 對象

JavaScript 在事件處理函數中會提供事件對象 event,我們修改下 getUserAction 函數:

function getUserAction(e) {
    console.log(e);
    container.innerHTML = count++;
};      

JavaScript 在事件處理函數中會提供事件對象 event,我們修改下 getUserAction 函數:

function getUserAction(e) {
    console.log(e);
    container.innerHTML = count++;
};// 第三版
function debounce(func, wait) {
    var timeout;    return function () {
        var context = this;
        var args = arguments;        clearTimeout(timeout)
        timeout = setTimeout(function(){
            func.apply(context, args)
        }, wait);
    }
}      

傳回值

// 第四版
function debounce(func, wait) {
    var timeout, result;    return function () {
        var context = this;
        var args = arguments;        clearTimeout(timeout)
        timeout = setTimeout(function(){
            result = func.apply(context, args)
        }, wait);        return result;
    }      

我們修複了三個小問題:

  1. this 指向
  2. event 對象
  3. 傳回值

立刻執行

我不希望非要等到事件停止觸發後才執行,我希望立刻執行函數,然後等到停止觸發n秒後,才可以重新觸發執行。

// 第五版
function debounce(func, wait, immediate) {    var timeout, result;
    return function () {
        var context = this;
        var args = arguments;        if (timeout) clearTimeout(timeout);
        if (immediate) {
            // 如果已經執行過,不再執行
            var callNow = !timeout;
            timeout = setTimeout(function(){
                timeout = null;
            }, wait)
            if (callNow) result = func.apply(context, args)
        }
        else {
            timeout = setTimeout(function(){
                result = func.apply(context, args)
            }, wait);
        }        return result;
    }
}      

取消

// 第六版
function debounce(func, wait, immediate) {    var timeout, result;
    var debounced = function () {
        var context = this;
        var args = arguments;        if (timeout) clearTimeout(timeout);
        if (immediate) {
            // 如果已經執行過,不再執行
            var callNow = !timeout;
            timeout = setTimeout(function(){
                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;
}