编程题:实现函数防抖、函数节流 | 刷题打卡

139 阅读3分钟

函数防抖

一、概念

防抖和节流的概念并不是JS所特有的。他们是对函数持续调用时进行不同控制的两个概念。

用大白话来说,防抖就是防止用户手抖,从而避免用户无意间执行多次函数调用,进而导致一些意想不到的问题。

比如一个表单提交页面,在提交时用户不小心小手一抖,多次点击了提交按钮,好巧不巧后端又没有做幂等操作,这样就有可能有多条数据插入到数据库中了。

通过防抖可以实现在事件触发一定时间后没有再次触发同一时间时,再去执行相关的处理函数。

如果你去银行ATM机转账的时候观察就会发现,ATM机的数字输入键盘也做了防抖处理,你不能在很快的时间内连续输入2个同样的数字,必须等一定的时间之后才可以继续输入,这样做的好处是防止用户输入过快导致错误发生。

不信?那你去体验一下吧 😂

二、作用&应用场景

防抖主要能带来以下好处:

  1. 优化用户体验

  2. 提升页面性能

防抖的应用场景有:

  1. 输入框内容联想:适时反馈,减少服务器压力

  2. window.resize:避免UI渲染阻塞,导致浏览器卡顿

  3. 表单提交:减少服务器压力,防范恶意触发

三、如何实现防抖?

如上面所述,防抖是对一段时间的判断。所以我们可以通过一个计时器来实现。

具体来说,就是通过setTimeout来指定一定时间后执行处理函数,如果在这之前事件再次触发,则清空计时器,重新计时。

来,先写第一版:

function debounce(fn, wait) {
  let timerId = 0;
  return function(...args) {
    if (timerId) clearTimeout(timerId);
    timerId = setTimeout(()=>{
      fn.call(this, args);
    }, wait);
  }
}

// 调用
debounce(() => { console.log('触发函数调用')}, 200);

仔细观察会发现,方法调用后不会马上执行,而是等过了wait时间后才执行。那么如何才能首次触发立即执行呢?

function debounce(fn, wait) {
  let timerId = 0;
  return function(...args) {
    if (timerId){
      clearTimeout(timerId);
    } else{
      // 首次触发直接调用
      fn.call(this, args);
    }
    
    timerId = setTimeout(()=>{
      fn.call(this, args);
      timerId = 0;
    }, wait);
  }
}

四、测试用例

可以使用监听用户键盘的input、窗口的resize等事件,也可以监听鼠标移动事件。

function test() {
  var mousemove = debounce(() => { console.log('触发函数调用')}, 300);
  document.addEventListener('mousemove', mousemove, false);
}

test();

函数节流

一、概念

再看节流,throttle。节流的概念可以想象一下水坝,你建了水坝在河道中,不能让水流动不了,你只能让水流慢些。换言之,你不能让用户的方法都不执行。如果这样干,就是debounce了。为了让用户的方法在某个时间段内只执行一次,我们需要保存上次执行的时间点与定时器。

函数节流会用在比input, keyup更频繁触发的事件中,如resize, touchmove, mousemove, scrollthrottle 会强制函数以固定的速率执行。因此这个方法比较适合应用于动画相关的场景。

二、实现

// 版本一:首次不执行,通过计时器实现
function throttle(fn, delay) {
  let timerId = 0;
  return function () {
    if (timerId) return;
    timerId = setTimeout(() => {
      fn.apply(this, arguments);
      timerId = 0;
    }, delay);
  };
}

// 首次执行,通过时间戳比较实现
function throttle2(fn, delay) {
  let last = 0;
  return function () {
    let cur = +new Date();
    if(cur - last > delay) {
      fn.apply(this, arguments);
      last = cur;
    }
  }
}

三、测试

function test() {
  var mousemove = throttle(() => { console.log('触发函数调用')}, 500);
  document.addEventListener('mousemove', mousemove, false);
}

test();

四、总结

如果还是不明白的话可以体验一下这个在线可视化比较工具