Skip to content
On this page

函数防抖与节流

介绍

函数防抖和函数节流是优化高频率执行js代码的一种手段,js中的一些事件如浏览器的resize、scroll,鼠标的mousemove、mouseover,input输入框的keypress等事件在触发时,会不断地调用绑定在事件上的回调函数,极大地浪费资源,降低前端性能。为了优化体验,需要对这类事件进行调用次数的限制。

我们尝试完成一个需求:当鼠标在div上移动时,使它内部的数值不断+1

html
<style>
#box {
  width: 100%;
  height: 400px;
  background-color: royalblue;
  font-size: 100px;
  color: #fff;
  text-align: center;
  line-height: 400px;
}
</style>
<div id="box"></div>
<script>
// 定义一个初始值
let num=1;
let oDiv=document.getElementById('box')
// 事件函数
function handle(){
  oDiv.innerHTML = num++
  console.log(num)
}
oDiv.onmousemove = handle
</script>

作出上面例子我们发现:在移动鼠标的时候,会不断的触发handle函数导致资源浪费,如果此时又需搭配异步请求的话很容易发生阻塞,有没有什么方法能够让事件触发的频率不那么快呢?函数的防抖和节流就是解决这种事件的方案!

防抖

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

以上面的代码为例,我们将js部分改为:

js
let num = 1
let oDiv = document.getElementById('box');

// 事件函数
function handle() {
  oDiv.innerHTML = num++
  console.log(num);
}

// 防抖函数
function debounce(fn, time) {
  // 定义一个定时器
  let play;
  // 为了确保下面操作的是同一个定时器,这里我们需要使用闭包
  return function () {
    // 如果这个定时器已经存在就清除它
    if (play) clearTimeout(play)
    play = setTimeout(function () {
      // 这里如果调用的函数不需传参可以直接fn(),如果需要传递参数则需搭配apply方法
      fn.apply(this,arguments)
    }, time)
  }
}
// 鼠标移动停止后1s触发
oDiv.onmousemove = debounce(handle, 1000)

节流

在一段时间内,只会执行一次函数。

同样以上面的代码为例,我们将js部分改为:

js
let num = 1;
var oDiv = document.getElementById('box')

// 事件函数
function handle() {
  oDiv.innerHTML = num++;
  console.log(num);
}

// 节流函数
function throttle(fn, time) {
  let play;
  return function () {
    // 如果定时器存在,直接返回什么都不做
    if (play) return
    play = setTimeout(function () {
      fn.apply(this,arguments)
      // 执行完之后把play设置为null方便下次执行
      play = null;
    }, time)
  }
}
// 鼠标不管怎么移动,触发handle函数的间隔最低为1s
oDiv.onmousemove = throttle(handle, 1000)

上面是由定时器完成的节流,我们也可以使用时间戳的方式实现:

js
let num = 1;
var oDiv = document.getElementById('box')

// 事件函数
function handle() {
  oDiv.innerHTML = num++;
  console.log(num);
}

// 节流函数
function throttle(fn, time) {
  let t=0;
  return function(){
    let now=new Date();
    // 当前时间-上次触发事件的时间>间隔时间时才允许调用函数
    if(now-t>time){
      fn(this,arguments)
      // 将变量t赋值为最后一次触发事件的时间
      t=now
    }
  }
}
oDiv.onmousemove = throttle(handle, 2000)