【前端帮帮忙】第7期 关于节流(throttle)和防抖(debounce)的理解

节流和防抖在我们平时的项目中挺常用的,也是面试中经常会被提问的知识点,今天我们一起来学习一下。

节流

简单理解就是:控制函数每隔n秒执行一次。

作用

防止用户高频率的触发事件,刚好这个事件又需要处理大量的计算和渲染而带来的性能问题。

应用场景

  • onscroll
  • onresize
  • mousemove
  • touchmove

示例

这边我们以onscroll来写个例子,加深理解。

比如有这样一个场景:我们需要判断浏览器滚动条滚动到底部的时候去动态加载一些数据,可能我们直接就会写上以下的代码:

let obj = document.querySelector('.throttle-test');
let _count = 0;

obj.onscroll = function () {
  // 假设已经滚动到底部了,我们给_count加1。
  _count++;
  console.log('执行次数:' + _count);
}

我们来看下浏览器的打印结果,看以下gif图:

可以看到,函数执行了20次,很显然这并不是我们想要的,因为onscroll事件并不会等你滚动到底部了再去触发事件,而是会不间断的触发,这就很容易引发一些性能问题,这时候就需要用到节流了。

我们把代码做下修改:

let obj = document.querySelector('.throttle-test');
let _count = 0;
let _canRun = true;
obj.onscroll = function () {
  if (!_canRun) {
    return false;
  }
  _canRun = false;
  setTimeout(function () {
    // 假设已经滚动到底部了,我们给_count加1。
    _count++;
    console.log('执行次数:'+_count);
    _canRun = true;
  }, 500);
}

通过以下gif图,我们可以看到,函数最终只执行了2次。

通过一个定时器,我们控制函数每隔500毫秒再执行一次,大大降低了执行频率,从而提升性能。

节流概念理解

节流跟防抖,它们既有相似之处但又有所不同,很容易混淆。这里通过比喻来加深理解,先来说说节流。

节流的概念可以想象一下水坝,你建了水坝在河道中,不能让水流动不了,你只能让水流慢些。换言之,你不能让用户的方法都不执行。(个人比较喜欢这个比喻,因为它很形象的说出了跟防抖的区别。)

防抖

控制函数在n秒内只能执行一次,如果用户在n秒内重复的触发事件,则重新计时且函数不会被执行,只有等到用户不再触发事件的时候才去执行一次。

作用跟节流类似,也是为了防止用户高频率的触发事件所引发的性能问题。

防抖概念理解

可以想象一下做电梯,当有人进入电梯(触发事件),那电梯将在10秒后出发(执行事件),这时如果又有人进入电梯了(在10秒内再次触发了事件),我们又得重新等10秒才能触发(重新计时)。

示例

有个文本框让用户填写用户名,当用户输入字符时,我们需要实时发请求到后台去验证用户名是否有重复的。实际上,在加入防抖机制前,用户输入helloworld后,我们已经发送了10次请求了,很显然是不可取的。

看代码:

let obj = document.querySelector('#testInput');  // 获取文本输入框
let _count = 0;

obj.onkeyup = function () {
  _count++;
  console.log('执行次数:' + _count);
}

我们来给代码加入防抖机制:

我们只能假设用户在停顿n秒内没有再触发事件,我们就判定用户已经输入完成了,这时再发送请求。

看代码:

let obj = document.querySelector('#testInput');
let timer = null;
let _count = 0;

obj.onkeyup = function () {
  clearTimeout(timer);  // 清除定时器,重新计时
  timer = setTimeout(function () {
    _count++;
    console.log('执行次数:'+_count);
  }, 800);
}

通过动图,可以看到,当我一直输入的时候,事件是不会被触发的,直到我停止输入才会触发一次。

实现思路:我们把目标代码放入到一个定时器里,如果事件被频繁的触发,目标代码将不会被执行。为什么不执行呢,因为我们前面加了clearTimeout。相当于中途不断的有人进入电梯,电梯又得重新倒计时10秒才会启动一样,直到用户没再输入了(没人再进入电梯了),这时候目标代码才会按照我们设定的时间再去执行一次(电梯才会启动)。

节流和防抖的区别

  • 节流:目标代码会按照我们设定的时间间隔即每隔n秒就执行一次
  • 防抖:在用户不触发事件时,才去执行目标代码,并且抑制了本来在事件中要执行的动作;当事件被一直触发的情况下,目标代码有可能不会被执行
  • 函数节流会用在比input,keyup更频繁触发的事件中,如resize, touchmove, mousemove, scroll节流会强制函数以固定的速率执行。因此这个方法比较适合应用于动画相关的场景。

最后

感谢您的阅读,希望对你有所帮助。文中如果有描述不当的地方,烦请指正,感激不尽。
另外文中所演示的代码仅用来测试使用,并不适合用在实际开发中,实际开发可以使用Lodash库中的节流防抖方法,这里就不贴代码了,毕竟考虑的比较全面哈。

Lodash库的地址:

节流:https://www.lodashjs.com/docs/4.17.5.html#throttle
防抖:https://www.lodashjs.com/docs/4.17.5.html#debounce

关注

欢迎大家关注我的公众号前端帮帮忙,一起交流学习,谢谢~

参考:
https://zhuanlan.zhihu.com/p/38313717