JS的防抖和节流

在浏览器中如果一个事件被频繁触发,比如输入框的 keyup, 窗口的 resize 以及 scroll 事件等,如果不做任何处理,事件的回调函数将会对应执行,这必然会加重浏览器的负担,影响用户的体验。面对这种场景,我们可以用函数的防抖 (debounce) 和节流 (throttle) 来处理。

函数防抖

防抖 debounce: 让事件触发时的回调在一定的延时后执行。如果在计时期间又触发了事件,则重新开始计时。

比如在做一个检索的输入框,输入的内容发送给后台查询。如果不做防抖处理我们来看下:

<input type="text" id="input">

用打印来模拟请求处理:

function handle(content) {
    console.log(content);
}

var ipt = document.getElementById('input');
ipt.addEventListener('keyup', function(e){
    handle(e.target.value);
})

结果如下:

可见,每次触发事件都会执行回调函数,现在加入防抖处理:

function handle(content) {
    console.log(content);
}

function debounce(fn, delay) {
    var timer = null;
    return function (arg) {
        var that = this;
        clearTimeout(timer);
        timer = setTimeout(function(){
            fn.call(that, arg);
        },delay);
    }
}

var ipt = document.getElementById('input'),
    debounceHandle = debounce(handle, 500);

ipt.addEventListener('keyup', function(e){
    debounceHandle(e.target.value);
})

效果如下:

可见,输入框在停止输出500ms后执行回调。在防抖后的回调函数用 timer 记录计时,每次执行回调的时候会先清空之前的计时。注意这里的timer是闭包变量,始终保持着上一个计时。

函数节流

节流throttle: 让事件的回调一定时间间隔只执行一次。节流函数有两种实现方式,一种是记录增量,一种是定时方式。

用增量的方式实现节流:

function throttle(fn, delay) {
    var pre = Date.now();
    return function (arg) {
        var now = Date.now(),
            that = this;
        // 距离上次执行回调超过延时
        if(now - pre >= delay) {
            fn.call(that, arg);
            pre = now;
        }
    }
}

var ipt = document.getElementById('input'),
    throttleHandle = throttle(handle, 1000);

ipt.addEventListener('keyup', function(e){
    throttleHandle(e.target.value);
})

节流的效果如下:

可见,无论怎么输入,事件回调总在1s内执行一次。而且第一次输入会马上执行,这是因为处理节流的时候和第一次触发的时间间隔大于1s。但是最后一次触发不会执行回调。

利用计时方式处理节流:

function throttle(fn, delay) {
    var timer = null;
    return function (arg) {
        var that = this;
        if(!timer) {
            timer = setTimeout(function() {
                fn.call(that, arg);
                timer = null;
            }, delay);
        }
    }
}

利用变量 timer 记录定时器,如果定时器存在,则不执行回调。否则创建一个延时器执行回调。这种方法和时间戳增量的区别就是第一个触发不会立即执行回调,但是最后一次时间会在延时后触发回调函数。

如果想要立即触发并且最后一次也要执行回调,可以利用时间戳和计时方式结合在实现节流:

function throttle(fn, delay) {
    var timer = null, pre = Date.now();
    return function (arg) {
        var that = this, 
            now = Date.now(),
            remain = delay - (now - pre);

        clearTimeout(timer);
        if(remain <= 0) {
            fn.call(that, arg);
            pre = now;
        }else {
            timer = setTimeout(function(){
                fn.call(that, arg);
            }, remain);
        }
    }
}

上面的节流函数会先判断剩余的间隔时间,如果剩余时间小于0,则立即执行。否则创建一个剩余时间的定时。注意,每次要记得清空之前的定时。

应用场景

防抖 debounce 一般用来在输入检索,节约请求的资源。还有窗口的 resize,让不断调节窗口大小的最后一次触发。

节流 throttle 一般用在鼠标不断点击,让点击的回调按间隔执行一次。还有滑动 scroll 事件, 比如滚动到底部查询,按间隔请求一次数据来显示。

参考文章

https://juejin.im/post/5b8de829f265da43623c4261

https://mp.weixin.qq.com/s/Vkshf-nEDwo2ODUJhxgzVA

你可能感兴趣的:(JS/Node,js,函数防抖)