一文读懂函数的防抖和节流

什么是防抖和节流?

「 防抖 」

场景:输入名称的同时去服务器校验名称是否重复,如果代码没做限制,输一次发一次请求;多次点击触发事件

在事件被触发n秒后在执行回调函数,如果在n秒内又触发,则重新计时

 假设一个场景:鼠标划过一个div,触发onmousemove事件,它内部的文字会显示当前鼠标的坐标。

防抖:函数防抖,这里的抖动就是执行的意思,而一般的抖动都是持续的,多次的。假设函数持续多次执行,我们希望它冷静下来再执行。也就是当持续触发事件的时候,函数是完全不执行的,等最后一次触发结束的一段时间之后,再去执行。

分解一下需求:

  • 持续触发不执行
  • 不触发的一段时间之后在执行

 那么怎么实现上述的目标呢?

  •  在不触发的时间内在执行,那就需要一个定时器,
  •  定时器里面调用我们要执行的函数,将arguments传入,封装一个函数,让持续触发的事件监听我们封装的这个函数,将目标函数作为回调传进去,第二点就实现了
  •  再看第一点:持续触发不执行。我们先思考一下,是什么让我们的函数执行了呢?是上边的setTimeout。
  •  OK,那么现在的问题就变成了持续触发,不能有 setTimeout,这样直接在持续触发的时候,清掉计时器就好了。

「 节流  」

节流的会用在input,keyup更频繁触发事件中如resize、touchmove。「节流」会强制函数以固定的速率执行。节流的概念可以想象一下水坝,你建了水坝在河道中,不能让水流动不了,你只能让水流的慢一些。换言之,你不能让用户的方法都不执行,如果这样干,就是「防抖」了。

 节流的意思是让函数有节制的执行,而不是毫无节制的触发一次就执行一次。什么叫有节制呢?就是在一段时间内触发一次。

 分解一下需求:

  •  持续触发并不会执行多次
  •  到一定时间再去执行

 那么怎么实现上述的目标呢?

  •  持续触发,并不会执行,但是时间到了就会执行。抓取一个关键的点执行的时机」。
  •  要做到控制执行的时机,我们可以通过一个开关,与定时器setTimeout结合完成。
  •  函数执行的前提条件是开关打开,持续触发时,持续关闭开关。
  •  等到setTimeout到时间了再把开关打开,函数就会执行了。

 注:这里的throttle函数执行的结果是其内部return的function的调用,也就是说鼠标经过的事件监听实际上是这个被return的function,不断持续触发的是它。

       而 throttle函数只是提供了一个作用域,内部闭包声明一个run的开关变 量。

       由于闭包存在run这个变量会一直存在不被销毁,而let run = true只在这个闭包(局部作用域)内只声明了一次,但它不会被持续执行,所以return的函数内部的判断不会被它覆盖掉。

 总结:

 防抖和节流巧妙地用了setTimeout,来控制函数执行的时机,优点很明显,可以节约性能,不至于多次触发复杂的业务逻辑而造成页面卡顿。

 

 附上完整代码:

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<style>
  #container {
    width: 500px;
    height: 200px;
    background: #ccc;
    font-size: 20px;
    text-align: center;
    line-height: 200px;
  }
</style>
<body>
  <div id="container"></div>
</body>
<script>
// 防抖
function debounce(func, delay){
  let timeout;
  return function() {
    clearTimeout(timeout)
    timeout = setTimeout(()=>{
      func.apply(this, arguments)
    },delay)
  }
}
const container = document.querySelector('div');
// 调用方法
container.onmousemove = debounce(function(e){
  container.innerHTML = `clientX = ${e.clientX},clientY ${e.clientY}`
},1000);

// 节流
function throttle (func, delay  ) {
  let run = true;
  console.log ('触发的时候', run);
  return function ( ) {
    console.log ('return的函数内容', run); 
    if(!run) {
      return false // 如果关闭了开关,就不执行下面的代码
    }
    console.log ('判断开关关闭与否之后的', run);
    run = false; // 持续触发的话, run一直是false,就会停在上边的判断那里
    console.log ('开关关闭后', run);
    setTimeout(() => {
      func.apply(this, arguments);
      run = true;
      console.log ('开关打开后', run);
    }, delay)
  }
}
const container = document.querySelector('div');
// 调用方法
container.onmousemove = throttle(function(e){
  container.innerHTML = `clientX = ${e.clientX},clientY ${e.clientY}`
},1000);
</script>
</html>

 

 

 

 

在 PDFlux 中打开
无数据
posted @ 2020-04-15 23:52  翠花菇凉  阅读(304)  评论(0编辑  收藏  举报