debounce & throttle

debounce

原理

监听一个输入框,文字变化后触发change事件,如果直接用keyup事件,则会频繁触发change事件
防抖:用户输入结束或暂停的时候,才会触发change事件
应用场景: search框联想,注册的时候提示是否已注册等

<body>
  <div class="test">
    <input id='input1'>
  </div>
  <script>
    const input1 = document.getElementById('input1')
    // 糟糕的情况 每次输入文字都会输出值
    input1.addEventListener('keyup', function () {
      
      
      console.log(input1.value);
    })
  </script>

</body>

希望停半秒钟(自定义)发送一个请求,再输入也是停半秒钟的时候发送一个请求
这种机制称为防抖

在这里插入图片描述

<body>

  <div class="test">
    <input id='input1'>
  </div>
  <script>
    const input1 = document.getElementById('input1')
    // 糟糕的情况 每次输入文字都会输出值
    // input1.addEventListener('keyup', function () {
      
      
    //   console.log(input1.value);
    // })

    let timer = null
    input1.addEventListener('keyup', function () {
      
      
      // 如果有timer就清掉它
      timer&&clearTimeout(timer)
      timer = setTimeout(()=>{
      
      
        // 触发事件
        ajax()
        // 清空定时器
        timer=null
      },500)
    })

    function ajax() {
      
      
      console.log("ajax---",input1.value);
    }
  </script>

</body>

现在输入123(间隔小于0.5s) 来根据原理图看一下他的代码执行顺序
请添加图片描述

封装debounce函数

    const input1 = document.getElementById('input1')

    input1.addEventListener("keyup", debounce(function(){
    
    
      console.log(input1.value);
    },600))

    function debounce(fn, delay = 500) {
    
    
      // 注意timer是在闭包中的
      // 扩展  闭包一般在什么情况下用呢 ?  这个是作为函数作用域返回值
      // 外部不会轻易拿到 所以不会导致timer逻辑混乱
      let timer = null
      return function () {
    
    
        if (timer) {
    
    
          clearTimeout(timer)
        }
        timer = setTimeout(() => {
    
    
          fn()
          // fn函数在执行的时候,会把当前的this,arguments传进来
          // 返回的fn可能会有一些参数,还要把this传进来,如果用this的话那就不能
          // 用箭头函数了 只能用function形式了
          // fn.apply(this,arguments)
      
          timer = null
        }, delay)
      }
    }

总代码

 <!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>

  <div class="test">
    <input id='input1'>
  </div>
  <script>
    const input1 = document.getElementById('input1')
    let a =123
    input1.addEventListener("keyup", debounce(ajax,600,a))
    function debounce(fn, delay = 500) {
      
      
      let timer = null
      return function () {
      
      
        if (timer) {
      
      
          clearTimeout(timer)
        }
        timer = setTimeout(() => {
      
      
          fn.apply(this,arguments)
          timer = null
        }, delay)
      }
    }
    function ajax(aa) {
      
      
      console.log("ajax---", input1.value);
    }
  </script>

</body>

</html>

throttle

原理

保持一个频率连续触发

使用场景,拖拽一个元素的时候,要随时拿到该元素被拖拽的位置,比如拖拽排序,需要随时判断位置是否到达了要拖拽到的位置
如果直接用drag事件,则会频繁触发,很容易导致卡顿
这时候需要节流,无论拖拽速度多快,都会每隔100ms触发一次 对计算机来说 这是一个比较宽裕的时间

没有经过节流的代码

<div class="div1" draggable="true">拖拽</div>
    const div1 = document.getElementsByClassName('div1')[0]
    console.log(div1);
    div1.addEventListener("drag",(e)=>{
    
    
      // console.log(e);
      console.log(e.offsetX+' '+e.offsetY);
    })

在这里插入图片描述
经过节流的代码

    const div1 = document.getElementsByClassName('div1')[0]
    // console.log(div1);
    let timer = null
    div1.addEventListener("drag",(e)=>{
    
    
      // console.log(e);
      if(timer){
    
    
        return 
      }
      timer = setTimeout(() => {
    
    
        console.log(e.offsetX+' '+e.offsetY);
        timer = null
      }, 500);
     
    })

在这里插入图片描述

这个代码应该好懂
拖拽事件不断被触发,只要timer有值就return 等到设定的时间到了,timer清空,这时候才可以重新计时

封装throttle函数

     // 节流封装函数
     function throttle(fn, delay = 500) {
    
    
      let timer = null
      return function () {
    
    
        if (timer) {
    
    
         return 
        }
        timer = setTimeout(() => {
    
    
          fn.apply(this,arguments)
          timer = null
        }, delay)
      }
    }
   div1.addEventListener("drag", throttle(function (e) {
    
    
      console.log(e.offsetX);
    }, 100));

apply

apply作用,第一个绑定this,第二个绑定参数
参数在什么地方有用呢
这里是没有问题的

    div1.addEventListener("drag",function(e) {
    
    
      console.log(e);
    });

但是在这里
在这里插入图片描述

e会传给谁?他会传给throttle返回的函数
在这里插入图片描述
这个函数接收e
他不会传给throttle包裹的函数
在这里插入图片描述
那如何让包裹的函数也有e呢
那就需要通过apply把arguments传进去
如果是settimeout里面运行fn的话
那么e会是undefined
在这里插入图片描述
一篇好文章
https://www.zhihu.com/people/peng-leonard/posts

遇到的问题

疑问 根本就不会进入到回调函数里面
在这里插入图片描述
但是这样就可以
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/xiaozhazhazhazha/article/details/121848221