防抖

开发中,经常会遇到一些频繁触发的事件。

应用场景

  1. window: resize、scroll
  2. mousemove、mouseodown
  3. keyup, keydown

我们会明明鼠标只是移动一次,可能就会触发五六次。如果我们请求数据,1次就会请求这么多次,多么的浪费带宽。我们需要借助一些工具,使得我们能够控制触发的频率。

防抖

防抖就是我们的事件触发只会 n 秒后触发一次,如果你在事件触发当中,又触发了事件,事件的最终触发触发事件将会延迟到最后一次触发的 n 秒之后触发。

实现原理

原理就是:第一次触发我们开启一个delay秒的定时器,定时器到了我们就触发这个事件。
第一次 timer = 1. 但是又一次触发了,这个时候 if (timer) 就会执行,清除 timer 。
重新开启一个 delay 的定时器继续等待。一次又一次。

第一版本实现

注意: 真正的 this 指向,event事件对象都是在返回的包装还是当中。我们需要重新指回 func 函数中。

function debounce(func, delay) {
    var timer;
    return function () {
        var context = this,
            args = arguments;
        if (timer)  clearTimeout(timer);
        timer = setTimeout(function () {
            func.apply(context, args);
        }, delay);
    }
}

第二版 (立即执行事件)

immediate 如果需要立即执行,如果已经执行过,不再执行。

function debounce(func, delay, immediate) {
    var timer;
    return function () {
        var context = this,
            args = arguments;
        if (timer) clearTimeout(args);
        if (immediate) {
            var callNow = !timer;
            timer = setTimeout(function () {
                timer = null;
            }, delay);
            if (callNow) func.apply(context, args);
        } else {
            // 不用立即执行
            timer = setTimeout(function () {
                func.apply(context, args);
            }, delay);
        }
    }
}

返回值

function debounce(func, delay, immediate) {
    var timer, result;
    return function () {
        var context = this,
            args = arguments;
        if (timer) clearTimeout(args);
        if (immediate) {
            var callNow = !timer;
            timer = setTimeout(function () {
                timer = null;
            }, delay);
            if (callNow) result = func.apply(context, args);
        } else {
            // 不用立即执行
            timer = setTimeout(function () {
                func.apply(context, args);
            }, delay);
        }
        return result;
    }
}

中途取消

function debounce(func, delay, immediate) {
    var timer, result;
    var debounced = function () {
        var context = this,
            args = arguments;
        if (timer) clearTimeout(args);
        if (immediate) {
            var callNow = !timer;
            timer = setTimeout(function () {
                timer = null;
            }, delay);
            if (callNow) result = func.apply(context, args);
        } else {
            timer = setTimeout(function () {
                func.apply(context, args);
            }, delay);
        }
        return result;
    }

    debounced.cancel = function () {
        clearTimeout(timer);
        timer = null;
    }

    return debounced;
}