一、前言
当用户高频触发某一事件时,如窗口的resize、scroll,输入框内容校验等,此时这些事件调用函数的频率如果没有限制,可能会导致响应跟不上触发,出现页面卡顿,假死现象。此时,我们可以采用防抖(debounce) 和 节流(throttle) 的方式来减少调用频率,同时又不影响实际效果。
本质上是优化高频率执行代码的一种手段
二、防抖
防抖:就是指触发事件后,函数在 n 秒后只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数的执行时间。
简单的说,当一个函数连续触发,只执行最后一次。
案例理解
防抖的场景:小谢催小尹还钱,小尹决定三天后还小谢的钱,但是小谢催款的话,小尹就重新计算这个三天的期限,20号催款,23还,21号再催,就24号还。除非小谢这三天没有催小尹还钱。
函数防抖一般用在什么情况之下呢?一般用在,连续的事件只需触发一次回调的场合。具体有:
1、搜索框搜索输入。只需用户最后一次输入完,再发送请求;
 2、用户名、手机号、邮箱输入验证;
 3、浏览器窗口大小改变后,只需窗口调整完后,再执行resize事件中的代码,防止重复渲染。
代码实现
在下面这段代码中,我们实现了最简单的一个防抖函数,我们设置一个定时器,你重复调用一次函数,我们就清除定时器,重新定时,直到在设定的时间段内没有重复调用函数。
// fn是你要调用的函数,delay是防抖的时间
function debounce(fn, delay) {
  // timer是一个定时器
  let timer = null;
  // 返回一个闭包函数,用闭包保存timer确保其不会销毁,重复点击会清理上一次的定时器
  return function () {
    // 调用一次就清除上一次的定时器
    clearTimeout(timer);
    // 开启这一次的定时器
    timer = setTimeout(() => {
      fn();
    }, delay)
  }
}
 
代码优化
仔细一想,上面的代码是不是有什么问题?
问题一: 我们返回的fn函数,如果需要事件参数e怎么办?事件参数被debounce函数保存着,如果不把事件参数给闭包函数,若fn函数需要e我们没给,代码毫无疑问会报错。
问题二: 我们怎么确保调用fn函数的对象是我们想要的对象?你发现了吗,在上面这段代码中fn()函数的调用者是fn所定义的环境,这里涉及this指向问题,想要了解为什么可以去了解下js中的this。
 为了解决上述两个问题,我们对代码优化如下
// fn是你要调用的函数,delay是防抖的时间
function debounce(fn, delay) {
  // timer是一个定时器
  let timer = null;
  // 返回一个闭包函数,用闭包保存timer确保其不会销毁,重复点击会清理上一次的定时器
  return function () {
    // 保存事件参数,防止fn函数需要事件参数里的数据
    let arg = arguments;
    // 调用一次就清除上一次的定时器
    clearTimeout(timer);
    // 开启这一次的定时器
    timer = setTimeout(() => {
      // 若不改变this指向,则会指向fn定义环境
      fn.apply(this, arg);
    }, delay)
  }
}
 
三、节流
节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效
简单来说节流就是限制一个函数在一段时间内只能执行一次,过了这段时间,在下一段时间又可以执行一次。
案例理解
 节流的场景:小尹催小谢还钱,小谢决定三天内还小尹的钱,就算小尹这三天怎么催,小谢都会在第一次听到要还钱后的第三天,把钱还了。20号第一次催款,不论21,22号有没有催,小谢都会在23号把钱还给小尹。
应用场景如:
1、输入框的联想,可以限定用户在输入时,只在每两秒钟响应一次联想。
 2、搜索框输入查询,如果用户一直在输入中,没有必要不停地调用去请求服务端接口,等用户停止输入的时候,再调用,设
 置一个合适的时间间隔,有效减轻服务端压力。
 3、表单验证
 4、按钮提交事件。
代码实现
// 时间戳 & 定时器方法
function throttle(fn, delay) {
  // 初始化定时器
  let timer = null;
  // 上一次调用时间
  let prev = null;
  // 返回闭包函数
  return function () {
    // 现在触发事件时间
    let now = Date.now();
    // 触发间隔是否大于delay
    let remaining = delay - (now - prev);
    // 保存事件参数
    const args = arguments;
    // 清除定时器
    clearTimeout(timer);
    // 如果间隔时间满足delay
    if (remaining <= 0) {
      // 调用fn,并且将现在的时间设置为上一次执行时间
      fn.apply(this, args);
      prev = Date.now();
    } else {
      // 否则,过了剩余时间执行最后一次fn
      timer = setTimeout(() => {
        fn.apply(this, args)
      }, delay);
    }
  }
}
 
四、区别
相同点:
1、都可以通过使用 setTimeout 实现
 2、目的都是,降低回调执行频率。节省计算资源
不同点:
1、函数防抖,在一段连续操作结束后,处理回调,利用clearTimeout和 setTimeout实现。函数节流,在一段连续操作中,每一段时间只执行一次,频率较高的事件中使用来提高性能
2、函数防抖关注一定时间连续触发的事件,只在最后执行一次,而函数节流一段时间内只执行一次
例如,都设置时间频率为500ms,在2秒时间内,频繁触发函数,节流,每隔 500ms 就执行一次。防抖,则不管调动多少次方法,在2s后,只会执行一次
 




![[附源码]计算机毕业设计JAVA实验教学过程管理平台](https://img-blog.csdnimg.cn/a0c9c2426b3743619d880cd8dca1a29b.png)













