jannahuang / blog

MIT License
0 stars 0 forks source link

防抖和节流是什么 #21

Open jannahuang opened 2 years ago

jannahuang commented 2 years ago

防抖 debounce

当事件被触发,达到指定间隔后再执行任务。如果在指定间隔内又被触发,则重新计时,不会执行任务。

生活中的例子: 电脑设置 10 分钟后自动休眠,这期间操作电脑的话,会打断休眠程序。而当停止操作电脑,倒计时开始,当时间达到设定的 10 分钟之后,电脑休眠。

//原理:定时器是否被中断
function debounce(fn, delay) {
  let timer //初始化定时器
  return function () {
    if (timer) { //如果有定时器,则清除定时器,
      clearTimeout(timer)
    }
    timer = setTimeout(() => { //重新设定定时器
      fn.call(this, arguments) //当时间达到,则执行函数
    }, delay)
  }
}

节流 throttle

单次或多次触发事件之后,在指定间隔时间之内,只执行一次任务。

生活中的例子: 机场大巴每隔半小时发车一辆,不管上车了多少乘客,都是按时间点发车。

定时器实现节流

//原理:定时器是否存在
function throttle(fn, delay) {
  let timer //初始化定时器
  return function () {
    if (timer) { //如果有定时器,则不操作
      return
    }
    timer = setTimeout(() => { //设定定时器
      fn.call(this, arguments) //当时间达到,则执行函数
      timer = null // 然后清除定时器,当下次触发事件时,重新计时
    }, delay)
  }
}

时间戳实现节流

//原理:两次触发的时间差是否大于间隔时间
function throttle(fn, delay) {
  let previous = 0 //初始化时间,默认 0
  return function() {
    let now = new Date() //获取当前时间
    //注意第一次点击时会触发一次,因为 previous 初始值是 0
    if(now - previous > delay) { //时间戳运算
      fn.apply(this, arguments) // 当结果超过间隔时间,执行函数
      previous = now // 并且刷新上次时间为当前时间,以便下次运算
    }
  }
}

异同比较

相同点:

不同点:

常见应用场景

函数防抖的应用场景 连续的事件,只需触发一次回调的场景有:

函数节流的应用场景 间隔一段时间执行一次回调的场景有:

可以用以下代码测试:

<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>防抖 vs 节流</title>
</head>
<body>
  <button id="debounce">点我防抖</button>
  <button id="throttle">点我节流(定时器版)</button>
  <button id="throttle2">点我节流(时间戳版)</button>
  <script>
    window.onload = function() {
      // 1、获取这个按钮,并绑定事件
      var myDebounce = document.getElementById("debounce");
      myDebounce.addEventListener("click", debounce(sayDebounce, 1000));

      // 1、获取按钮,绑定点击事件
      var myThrottle = document.getElementById("throttle");
      myThrottle.addEventListener("click", throttle(sayThrottle, 1000));

      // 1、获取按钮,绑定点击事件
      var myThrottle2 = document.getElementById("throttle2");
      myThrottle2.addEventListener("click", throttle2(sayThrottle2, 1000));
    }

    // 2、防抖功能函数,接受传参
    function debounce(fn, delay) {
      // 4、创建一个标记用来存放定时器的返回值
      let timeout = null;
      return function() {
        // 5、每次当用户点击/输入的时候,把前一个定时器清除
        if(timeout) {
          clearTimeout(timeout);
        }
        // 6、然后创建一个新的 setTimeout,
        // 这样就能保证点击按钮后的 delay 间隔内
        // 如果用户还点击了的话,就不会执行 fn 函数
        timeout = setTimeout(() => {
          fn.call(this, arguments);
        }, delay);
      };
    }

    // 3、需要进行防抖的事件处理
    function sayDebounce() {
      // ... 有些需要防抖的工作,在这里执行
      console.log("防抖成功");
    }

    // 2、节流函数体(定时器版)
    function throttle(fn, delay) {
      // 4、创建一个标记用来存放定时器的返回值
      let timeout = null;
      return function() {
        // 5、在函数开头判断是否存在定时器,存在则中断函数
        if(timeout) {
          return;
        }
        // 6、创建定时器
        timeout = setTimeout(() => {
          fn.call(this, arguments);
          // 7、执行完事件(比如调用完接口)之后,清除定时器
          timeout = null;
        }, delay);
      };
    }

    // 3、需要节流的事件
    function sayThrottle() {
      console.log("节流成功(定时器版)");
    }

    // 2、节流函数体(时间戳版)
    function throttle2(fn, delay) {
      let previous = 0;
      return function() {
        let now = new Date() //获取当前时间
        if(now - previous > delay) { //时间戳运算
          fn.apply(this, arguments) // 当结果超过间隔时间,执行函数
          previous = now // 并且刷新上次时间为当前时间,以便下次运算
        }
      };
    }

    // 3、需要节流的事件
    function sayThrottle2() {
      console.log("节流成功(时间戳版)");
    }
  </script>
</body>
</html>

debounce-vs-throttle

以上笔记参考彻底弄懂函数防抖和函数节流JS 防抖与节流学会手写防抖节流 及其它参考资料