防抖与节流

防抖和节流

防抖(debounce)

在一定时间内连续触发同一事件,事件处理函数只执行一次,且是在最后一次触发的时候执行


现在有一个input标签:

<input type="text" >

监听输入事件,并打印出来输入的内容

let inp = document.querySelector("input");
inp.oninput = function() {
	console.log(this.value)
}

会发现这个事件执行了很多次:
防抖与节流
那么如果每一次输入都是跟后台进行一次数据交互,就会非常影响性能
怎么样才能让请求次数没那么多?

// 用户一输入,就会开启一个定时器,只要还在继续输入,就清除掉这个定时器,直到最后一次输入
let inp = document.querySelector("input");
let timer = null;	// 控制事件是否触发

inp.oninput = function() {
  // 进入此分支,说明当前正在一个计时过程中(0.5s内),而且又触发了此事件,所以要清除掉这个计时,重新开始计时
  if(timer !== null) {
    clearTimeout(timer);
  }
  // 进入了此分支表示当前并没有在计时,那就开启一个计时
  timer = setTimeout(() => {
    console.log(this.value);
  },500)
}

但是我们上面的代码不太好,首先是建了一个全局变量,而且我们的防抖代码和业务逻辑代码console.log(this.value);混在了一起

那么怎么解决呢?
我们可以利用闭包来封装一个防抖的函数

function debounce() {

}

希望实现的效果是:
用防抖来约束oninput事件,oninput事件执行的函数是这里传进来的参数function(){},我们的业务逻辑也写在里面,防抖的延迟是500ms

let inp = document.querySelector("input");
let timer = null;	// 控制事件是否触发

inp.oninput = debounce(function(){}, 500);

我们知道,oninput应该等于一个函数,

inp.oninput = function(){}

那么现在其实是将调用debounce的结果赋值给了oninput

inp.oninput = debounce(function(){}, 500) {} 

所以说debounce的返回值肯定是一个函数,它返回的值就是oninput这个事件执行的函数,而且我们还需要一个参数fnfn要传一个函数,里面写业务逻辑代码,另一个参数delay就是防抖的延迟时间

timer是外部函数的变量,return的是内部函数,内部函数使用了外部函数的变量,所以timer不会被销毁

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

我们这个时候调用debounce()来试试可不可以

inp.oninput = debounce(function() {
  console.log(this.value);
},500)

会发现没有正常输出,输出的是undefined
防抖与节流
我们将代码改成console.log(this)
此时这个this指向的不是inp,而是全局对象,this是由传进去的这个函数执行,传进去之后执行的其实是fn(),调用这个fn()的其实是window对象
防抖与节流
而我们return的那个内部函数function(){}才是由inp执行,所以我们要由inp调用fn()才行,用call改变调用fn的对象,改成用this调用,而this就是这个inp事件对象

timer = setTimeout(() => {
  // fn();
  fn.call(this);
},delay)

节流(throttle)

在一定时间内连续触发同一事件,函数立即执行一次,之后该函数在指定时间期限内不再工作,直至过了这段时间才能重新生效


给页面加个滚动条:

<style>
  body {
    height: 200px;
  }
</style>

监听一个滚动事件

window.onscroll = function() {
  console.log(123);
}

我们随便拉一下滚动条,这个事件会被执行非常多次,那如果我们这里的业务逻辑再复杂一些,或者要跟后台进行数据交互,这也会很影响性能
防抖与节流
我们对比一下防抖和节流:
防抖是只执行最后一次,节流是控制执行次数

// 执行完setTimeout将状态->true,没执行就将状态->false,也就是每0.5s才会再执行一次,就将执行的频率降低了
let flag = true; // 状态位
window.onscroll = function() {
  if(flag) {
    setTimeout(() => {
      console.log(123);
	  flag = true; 
    },500)
  }
  flag = false; // 休息时间
}

这个时候再随意滚动,还是会执行事件,但是次数大大减少
防抖与节流
封装:(和上面的防抖一样的逻辑)

function throttle(fn, delay) {
  let flag = true; // 状态位
  return function() {
    if(flag) {
      setTimeout(() => {
        fn.call(this);
        flag = true; 
	  },delay)
    }
	flag = false; // 休息时间
  }
}

调用:

window.onscroll = throttle(function(){
  console.log(123);
},500)

⚠️:节流并不只这一种实现方案,还可以完全不借助setTimeout,将状态位换成时间戳,利用时间戳差值是否大于指定的间隔时间来做判断


上一篇:实时显示鼠标在页面中的位置


下一篇:Linux编程入门三多线程