JavaScript 中防抖和节流的实现原理及应用场景

800 阅读4分钟

JavaScript 中防抖和节流的实现原理及应用场景

前言

开发中,经常会遇到以下场景:监听鼠标移动 onmousemove,监听页面滚动 onscroll,监听 input 输入,按钮搜索、提交功能的点击事件等。这些场景下,事件会被频繁触发,但我们并不想事件被频繁触发,这时就需要通过防抖和节流来限制频繁操作。

防抖和节流都是为了解决事件频繁触发的问题,都是为了降低资源的浪费,提高性能。 但在实现原理上有些不同,具体实现原理以及防抖和节流之间到底有什么样的区别,详见下文。

一、 防抖( debounce

定义: 在规定时间内持续触发事件,那么这个延时会被重置,不会执行函数内的事件处理函数,当在规定时间内不会再次触发该事件,则执行函数内的事件处理函数。也就是说,当用户一直触发该事件,且每次触发的事件间隔均小于规定延时,那么最终也只会执行一次最后一次的函数回调。

使用场景: 防抖的主要使用场景就是搜索框持续监听用户输入的内容。如果不使用防抖进行优化,那么会出现一个现象,就是当用户快速输入十个搜寻字符,对于用户来说,前九次搜索并不是用户想要的结果,但是服务器仍会重新发起九次请求,这并不是我们想要的。使用防抖优化后,前九次快速输入的请求,会被防抖函数拦截,只会在用户输入最后一个字符并等待了一段时间后(设定的防抖延时),只会发起最后一次请求。

防抖函数的实现:

// 1.封装防抖函数 **防抖重在清零 clearTimeout(timer)**
function debounce(fn, time) {
  // 4.创建一个标记用来存放定时器的返回值
  let timeout = null;
  return function () {
    // 5.每当用户触发input事件  把前一个 setTimeout 清楚掉
    clearTimeout(timeout);
    // 6.然后又创建一个新的 setTimeout, 这样就能保证输入字符后等待的间隔内 还有字符输入的话,就不会执行 setTimeout里面的内容
    timeout = setTimeout(() => {
      // 7.这里进行防抖的内容
      fn();
    }, time);
  };
}

// 2.获取inpt元素
var inp = document.getElementById("inp");
// 8. 测试防抖临时使用的函数
function sayHi() {
  console.log("防抖成功");
}
// 3.给inp绑定input事件  调用封装的防抖函数  传入要执行的内容与间隔事件
inp.addEventListener("input", debounce(sayHi, 5000));

二、节流( throttle

定义: 在持续触发的事件中,在规定延时内只执行一次事件处理函数。如果在规定延时内再次触发则会被忽略。节流的目的就是将频繁触发的事件转变为少量触发,降低服务器处理的频率以提高效率。

使用场景: 节流的使用场景,常见于一些频繁触发的业务,例如移动端页面的上拉刷新和下拉加载,搜索提交按钮的点击事件等一些频繁触发的使用场景。节流函数的使用本质上就是限制事件处理函数的处理次数,降低服务器使用频率,以实现优化性能。

节流函数的实现:

// 1.封装节流函数(使用定时器) 节流重在加锁 timer=timeout
function throttle(fn, delay) {
  //3. 通过闭包保存一个 "节流阀" 默认为false
  let timer = false;
  return function () {
    //8.触发事件被调用 判断"节流阀" 是否为true  如果为true就直接trurn出去不做任何操作
    if (timer) {
      return;
    } else {
      //4. 如果节流阀为false  立即将节流阀设置为true
      timer = true; //节流阀设置为true
      //5.  开启定时器
      setTimeout(() => {
        //6. 将外部传入的函数的执行放在setTimeout中
        fn.apply(this, arguments);
        //7. 最后在setTimeout执行完毕后再把标记'节流阀'为false(关键)  表示可以执行下一次循环了。当定时器没有执行的时候标记永远是true,在开头被return掉
        timer = false;
      }, delay);
    }
  };
}

三、总结

1.防抖 防止抖动,单位时间内事件触发会被重置,避免事件被误伤触发多次。代码实现重在清零 clearTimeout。

2.节流 控制流量,单位时间内事件只能触发一次,与服务器端的限流 (Rate Limit) 类似。代码实现重在开锁关锁 timer=timeout; timer=null。