Javascript防抖与节流示例详解

Javascript防抖与节流示例详解

Javascript防抖与节流示例详解

防抖节流本质上是优化高频率执行代码的一种手段

如:浏览器的 resizescrollkeypressmousemove 等事件在触发时,会不断地调用绑定在事件上的回调函数,极大地浪费资源,降低前端性能

为了优化体验,需要对这类事件进行调用次数的限制,对此我们就可以采用 防抖(debounce)  节流(throttle) 的方式来减少调用频率

定义

  • 防抖: n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时
  • 节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效

Lyove

防抖(debounce)

触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间。(可以将防抖类比成电梯:第一个人进电梯之后,电梯会在5秒之后自动关闭电梯门,如果在这5秒内又有人进来了,那么电梯会重新等待5秒后再关门。)

应用场景:input搜索框,用户连续输入,等输入停下来或结束时再去触发搜索接口

实现:

1、简单版本的实现

// fn 是事件处理程序,
// delay 是事件执行的延迟时间,单位:毫秒
function debounce(fn, delay){
  let timer = null;
  return function(...args) {
    const _this = this;
    // 每次触发事件 都把定时器清掉重新计时
    clearTimeout(timer);
    timer = setTimeout(function() {
      // 执行事件处理程序
      fn.call(_this, args);
    }, delay);
  }
}

2、立即执行版本实现,可加入第三个参数用于判断:

// fn 是事件处理程序
// delay 是事件执行的延迟时间,单位:毫秒
// immediate 是否立即执行
function debounce(func, delay, immediate) {
    let timeout;
    return function (...args) {
        const _this = this;
        if (timeout) {
            clearTimeout(timeout);
        }
        if (immediate) {
            let callNow = !timeout; // 第一次会立即执行,以后只有事件执行后才会再次触发
            timeout = setTimeout(() => {
                timeout = null;
            }, delay);
            if (callNow) {
                func.apply(_this, args);
            }
        } else {
            timeout = setTimeout(() => {
                func.apply(_this, args);
            }, delay);
        }
    }
}

节流(throttle)

当持续触发事件时,保证一定时间段内只调用一次事件处理函数。所以节流会稀释函数的执行频率

应用场景:按钮重复提交、滚动条事件、resize事件等高频监听事件

实现

1、使用时间戳实现

事件会立即执行,停止触发后没有办法再次执行

// fn 是事件处理程序
// delay 是事件执行的延迟时间,单位:毫秒
function throttle(fn, delay = 100) {
  // 定义初始时间(开始触发事件的时间)
  let oldTime = Date.now();
  return function(...args) {
    const _this = this;
    // 获取当前时间戳
    const newTime = Date.now();
    // 判断当前时间与初始时间是否超过间隔
    if (newTime - oldTime >= delay) {
      // 执行事件处理程序
      fn.call(_this,  args);
      // 更新初始时间
      oldTime = Date.now();
    }
  };
}

2、使用定时器实现

delay毫秒后第一次执行,第二次事件停止触发后依然会再一次执行

// fn 是事件处理程序,
// delay 是事件执行的延迟时间,单位:毫秒
function throttle(fn, delay = 100) {
  let timer = null;
  return function (...args) {
    const _this = this;
    if (!timer) {
      timer = setTimeout(() => {
        // 执行事件处理程序
        fn.call(_this, args);
        // 事件执行完后把定时器清除掉,下次触发事件的时候再设置
        timer = null;
      }, delay);
    }
  };
}

3、时间戳和定时器结合

function throttle(fn, delay = 100) {
    let timer = null;
    let startTime = Date.now();
    return function (... args) {
        const _this = this;
        // 当前时间
        let curTime = Date.now();
        // 从上一次到现在,剩余时间
        let remaining = delay - (curTime - startTime);
        clearTimeout(timer);
        if (remaining <= 0) {
            fn.apply(_this, args);
            starttime = Date.now();
        } else {
            timer = setTimeout(fn, remaining);
        }
    }
}

作者:Lyove

What's Your Reaction?

like

dislike

love

funny

angry

sad

wow