什么是节流和防抖,以及它们的使用场景

6,887 阅读6分钟

1. 节流

一段时间内只能触发一次,如果这段时间内触发多次事件,只有第一次生效会触发回调函数,一段时间过后才能再次触发(n秒内第一次生效)

例如:轮播图 98k点击一次射击一次换子弹期间不能射击 王者荣耀英雄技能冷却

代码实现:

function throttle(fn,delay){
  let flag = true;

  return function(){
    if(flag){
      flag = false;
      setTimeout(() =>{
        flag = true
      },delay)
      return fn()
    }
  }  
}

效果:即使一段时间内大量触发同一事件,在回调函数执行一次之后,该回调函数在指定的时间期限内不再工作,直至过了这段时间才重新生效。

2. 防抖

在事件被触发时,延迟n秒后再触发回调函数,如果n秒内又触发了事件,则会重新开始计算时间(n秒内最后一次生效)

例如:王者回城 输入框的搜索功能 页面resize事件

代码实现:

function debounce(fn,delay){
  let timer = null;
  return function(){
    if(timer){
      clearTimeout(timer)
    }
    timer = setTimeout(fn,delay)
  }
}

效果:在某个时间段内,不管你触发多少次事件,只执行一次回调函数。

3. 事例

写一个例子将上面的节流和防抖都用上💡

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .box{
      width90%;
      height200px;
      outline: blanchedalmond 1px solid;
      margin10px auto;
      padding20px;
      box-sizing: border-box;
    }
    .container{
      height60px;
      margin-top20px;
      display: flex;
      justify-content: space-between;
      align-items: center;
    }
    .aa{
      width60px;
      height:60px;
      text-align:center;  
      color: white;
    }
    .aa>div{
      width60px;
      height40px;
    }
    #target{
      background-color: coral;
    }
    #home{
      background-color: aqua;
    }
    #bullet{
      width60px;
    }
    #bullet>div{
      position: absolute;
      leftcalc(5% + 20px);
      width30px;
      height10px;
      background-color: dimgrey;
    }
    #hanxin{
      text-align: center;
      position: relative;
      left0px;
    }

    .send{
      animation: move 1s linear;
      animation-fill-mode: forwards;
    }
    @keyframes move
    {
      to {leftcalc(90% - 35px);}
    }
  </style>
</head>
<body>
  <div class="box">
    <h2>节流</h2>
    <button id="shoot">发射</button>
    <text id="text1">子弹已就绪</text>
    <div class="container">
      <div id="bullet">手枪
      </div>
      <div class="aa" id="target">目标
        <div></div>
      </div>
    </div>
  </div>
  <div class="box">
    <h2>防抖</h2>
    <button id="back">回城</button>
    <text id="text2">英雄未回城</text>
    <div class="container">
      <div id="hanxin">韩信</div>
      <div class="aa" id="home">泉水
        <div></div>
      </div>
    </div>
  </div>
</body>
<script>
let bullet = document.querySelector('#bullet');
let hanxin = document.querySelector('#hanxin');
let shoot = document.querySelector('#shoot');
let back = document.querySelector('#back');

let text1 = document.querySelector('#text1')
let text2 = document.querySelector('#text2')

// 节流
// 两个参数分别是 要执行的回调函数,间隔时间
function throttle(fn,delay){
  let flag = true;

  return function(){
    if(flag){
      flag = false;
      text1.textContent = '子弹上膛中'
      setTimeout(() =>{
        flag = true
        text1.textContent = '子弹已就绪'
      },delay)
      return fn()
    }
  }  
}

// 防抖
function debounce(fn,delay){
  let timer = null;
  return function(){
    if(text2.textContent === '英雄已回城'){
      return alert('韩信已经回城了,刷新页面再来试一下吧!')
    }
    if(timer){
      clearTimeout(timer)
    }
    text2.textContent = '英雄回城中'
    timer = setTimeout(fn,delay)
  }
}

// 射击事件
function shooting(){
  var zd  = document.createElement("div")
  bullet.appendChild(zd).classList.add('send')
}

// 回城事件
function goHome(){
  hanxin.style.left = 'calc(100% - 60px)';
  text2.textContent = '英雄已回城'
}

let a = throttle(shooting,2000)
shoot.addEventListener('click',a)

let b = debounce(goHome,3000)
back.addEventListener('click',b)
</script>
</html>

效果图:

可以看到子弹射出后,要等待一段时间⏱才能,再次射击。

当韩信回城时,我们再次点击回城按钮,会重新计算回城时间⏱

4. 总结

  • 节流是触发事件后,立即执行回调,一段时间过后才能再次触发,在这期间内无论触发多少次事件都不会执行回调(类似英雄技能冷却)。
  • 防抖是触发事件后,过一段时间再执行回调,在这期间再次触发事件,会重新计算时间(类似英雄回城)。
  • 节流和防抖的作用都是防止函数多次调用。