【李拜天的学习笔记】一千字帮你牢记防抖和节流,手写秒杀面试官

174 阅读6分钟

防抖(debounce)

什么是防抖?

比喻一下:我频繁地敲击键盘,你就敲吧,等你敲完最后一下,我们再进行下一步操作

防抖的字面意思就是防止抖动,比如当我们写一个输入框的时候,每次键盘的敲击都带来整个页面的反应,试想下频繁的敲击是不是就会带来页面的“抖动”。

所以防抖的原理就是:在事件触发n秒后再执行回调函数,如果在这n秒内再次触发事件,则重新计时

举个例子:

const printSomething = function(){
  for(let i = 0;i < 10;i++){
    console.log('i--------', i);
  }
};
// 获取输入框dom
const inputBox = document.getElementById('input')

inputBox.addEventListener('keyup', (e) => {
  printSomething();
})

如果像上面一样写代码,只要键盘有敲击便会无节制地执行回调函数,当然这里的回调函数很简单,只是打印0-10而已,试想如果回调函数需要打印0-100000呢?如此多的回调函数入栈必然会导致页面宕机。针对这种情况,我们希望的是:“只在最后一次敲击键盘后去执行回调函数”(你别抖了!),顺着这个想法,我们来改造一下:

const printSomething = function(){
  for(let i = 0;i < 10;i++){
    console.log('i--------', i);
  }
};

function debounce(func, delay){
  let timer = null;
  clearTimeout(timer);
  timer = setTimeout(function(){
    func();
  }, delay);
}

// 获取输入框dom
const inputBox = document.getElementById('input')

inputBox.addEventListener('keyup', (e) => {
  printSomething();
})

防抖的应用场景

  • 搜索框实时搜索:当用户在搜索框中输入关键字时,防抖可以在用户输入完毕后一段时间内才触发搜索请求,避免频繁地请求搜索数据,提高搜索性能和用户体验。
  • 窗口大小调整:当用户调整浏览器窗口大小时,防抖可以避免频繁地触发响应,以提高页面性能和用户体验。
  • 按钮点击事件:当用户点击按钮时,防抖可以避免多次点击,以防止意外的操作和减轻服务器负担。
  • 滚动加载:当用户滚动页面时,防抖可以避免频繁地触发加载请求,减轻服务器负担和提高用户体验。
  • 实时数据更新:当数据源实时更新时,防抖可以避免频繁地触发数据更新,以避免过多的请求和资源浪费。

手写防抖

通过对防抖的介绍,我们可以大致归纳出,手写一个防抖函数需要以下几个关键点:

  1. 接收两个参数,一个是“抖动结束后的执行函数”func,一个是最小防抖时间单位
  2. 使用一个计时器setTimeout来实现延迟操作
  3. 返回一个新函数,新函数会在延迟时间内等待,如果延迟时间内没有再次触发,则去执行func,如果触发了,则setTimeout重新开始计时
  4. 外部会对防抖函数频繁调用,所以debounce函数会执行多次
function debounce(func, delay) {
  let timeId;

  return function(...args) {
    if(timeId) {
      clearTimeout(timeId);
    }
    timeId = setTimeout(() => {
      func.apply(this, args);
    }, delay)
  }
}

节流(throttle)

什么是节流?

比喻一下:省点水吧~,我一分钟只开一下水闸,你拧多少次水龙头都没用

节流指的是在一定时间内只执行一次事件处理函数。如果在这段时间内多次触发事件,只有第一次触发的事件会执行处理函数,后面的事件会被忽略。节流可以减少事件处理函数的执行次数,提高性能和响应速度。

节流的应用场景

  • 监听滚动事件:当用户在网页中滚动页面时,会不断触发滚动事件。如果在滚动事件中执行大量操作,会导致页面卡顿和性能问题。可以使用节流技术,限制滚动事件处理函数的调用次数,以提高性能和用户体验。
  • 搜索框输入事件:当用户在搜索框中输入关键字时,会触发输入事件,以便动态搜索相关结果。如果在每次输入事件中都发送请求,会导致不必要的请求和服务器资源的浪费。可以使用节流技术,在一定时间内限制输入事件处理函数的调用次数,以减少请求和提高性能。
  • 窗口大小调整事件:当用户调整浏览器窗口的大小时,会触发窗口调整事件。如果在窗口调整事件中执行大量操作,会导致页面卡顿和性能问题。可以使用节流技术,限制窗口调整事件处理函数的调用次数,以提高性能和用户体验。
  • 监听鼠标移动事件:当用户在网页中移动鼠标时,会触发鼠标移动事件。如果在每次鼠标移动事件中执行大量操作,会导致页面卡顿和性能问题。可以使用节流技术,限制鼠标移动事件处理函数的调用次数,以提高性能和用户体验。

手写节流

实现节流的关键点与防抖有相似之处,关键区分在于函数的执行时机不同

  1. 节流函数接收两个入参,执行函数func与延迟时间delay
  2. 页面第一次进到节流函数的逻辑内,会直接执行func,并开始计时delay
  3. 在一次func执行之后的延迟时间delay内,不再触发func的执行
// 节流函数
function throttle(func, delay) {
  let timer = 0;
  return function (...args) {
    if (timer) {
      return;
    }
    timer = setTimeout(() => {
      fn.apply(this, args)
      timer = 0;
    }, delay) 
  }
}

此外,我们还可以用时间戳的方式来实现节流

function throttle(func, delay){
  let lastTime = 0;

  return function (...args) {
    const now = new Date().getTime();
    const timeDiff = now - lastTime;

    if (!lastTime || timeDiff > delay){
      lastTime = now;
      func.apply(this, args);
    }
  }
}

以上代码中,throttle函数接受两个参数:一个函数func和一个延迟时间delay,并返回一个新的函数。新函数会在指定的延迟时间内等待,如果在这段时间内再次调用新函数,则不会执行原始函数func,直到这段时间过去。

在新函数中,我们首先获取当前时间戳,计算当前时间与上一次调用时间的时间差。如果时间差大于或等于延迟时间delay,则执行原始函数func并更新上一次调用时间lastTime

const button = document.getElementById('my-button');
button.addEventListener('click', throttle(function () {
  console.log('Button clicked');
}, 500));

在此示例中,当用户点击按钮时,throttle函数将原始函数console.log封装在内部,最多每500毫秒执行一次该函数。这可以避免在短时间内连续点击按钮导致的过多操作。

总结

防抖和节流同样都是一种用于优化Web应用程序性能的方式。

防抖和节流都能够优化Web应用程序的性能,但它们的应用场景和效果有所不同。一般而言,防抖适用于需要等待用户输入的场景,以避免频繁的请求或操作。节流适用于高频率触发事件的场景,以避免过度执行操作导致性能问题。

为了避免混淆,可以类比记忆,比如:

防抖:你别抖了~,等你抖完我们再继续下一步

节流:省点水吧~,我一分钟只开一下水闸,你拧多少次水龙头都没用