JustYeh的前端博客

JavaScript节流和防抖

2019-07-02

js

节流throttle

设定一个时间间隔,某个频繁触发的函数,在这个时间间隔内只会执行一次。也就是说,这个频繁触发的函数会以一个固定的周期执行。

高频触发的函数每隔一个时间段执行执行,并且最后一次触发时也执行

适用场景:

  • 鼠标不断点击触发,mousedown(单位时间内只触发一次)
  • 监听滚动事件,比如是否滑到底部自动加载更多,用 throttle 来判断

时间戳版本

function throttle(func, delay){
  let prev = Date.now();
  return function(){
    const context = this;
    const args    = arguments;
    const now     = Date.now();
    if(now - prev >= delay){
      func.apply(context, args);
      prev = Date.now();
    }
  }
}

定时器版本

fucntion throttle(func, delay){
  let timer = null;

  return funtion(){
    let context = this;
    let args    = arguments;
    if(!timer){
      timer = setTimeout(function(){
        func.apply(context, args);
        clearTimeout(timer);
        timer = null;
      }, delay);
    }
  }
}

防抖debounce

debounce 函数封装后,返回内部函数;每一次事件被触发,都会清除当前的 timer 然后重新设置超时并调用。这会导致每一次高频事件都会取消前一次的超时调用,导致事件处理程序不能被触发,只有当高频事件停止,最后一次事件触发的超时调用才能在 delay 时间后执行

高频触发的函数只到最后一次触发时执行

适用场景:

  • search 搜索联想,用户在不断输入值时,用防抖来节约请求资源。
  • window 触发 resize 的时候,不断的调整浏览器窗口大小

立即防抖

事件停止触发后才执行

function debounce(fun, delay) {
    let timer = null;
    return function() {
        let context = this;
        let args = arguments;
        clearTimeout(timer);
        timer = setTimeout(function() {
            fn.apply(context, args);
        }, delay);
    };
}

非立即防抖

事件会立马触发一次,之后事件停止触发后再执行一次

function debouce(func,delay,immediate){
    var timer = null;
    return function(){
        var context = this;
        var args = arguments;
        if(timer) clearTimeout(time);
        if(immediate){
            //根据距离上次触发操作的时间是否到达delay来决定是否要现在执行函数
            var doNow = !timer;
            //每一次都重新设置timer,就是要保证每一次执行的至少delay秒后才可以执行
            timer = setTimeout(function(){
                timer = null;
            },delay);
            //立即执行
            if(doNow){
                func.apply(context,args);
            }
        }else{
            timer = setTimeout(function(){
                func.apply(context,args);
            },delay);
        }
    }
}

防抖和节流的结合

/**
 * 函数节流方法
 * @param Function fn 延时调用函数
 * @param Number delay 延迟多长时间
 * @param Number atleast 至少多长时间触发一次
 * @return Function 延迟执行的方法
 */
var throttle = function (fn, delay, atleast) {
    var timer = null;
    var previous = null;

    return function () {
        var now = +new Date();

        if ( !previous ) previous = now;
        if ( atleast && now - previous > atleast ) {
            fn();
            // 重置上一次开始时间为本次结束时间
            previous = now;
            clearTimeout(timer);
        } else {
            clearTimeout(timer);
            timer = setTimeout(function() {
                fn();
                previous = null;
            }, delay);
        }
    }
};

case

// case 1
window.onscroll = throttle(testFn, 200);
// case 2
window.onscroll = throttle(testFn, 200, 500);

在上面的例子中,case1 没有传递 atleast 参数,那么 testFn 只会带 scroll 停止后才会执行,即为防抖

case2 有传递 atleast 参数,变现为第一次会延迟 500ms 执行,之后会每间隔 500ms 执行,即为节流

当然,上面的例子没有处理参数和 this 绑定问题,有待优化