一文教你彻底学会JavaScript手写防抖节流

前言

  • 前段时间群友说面试的时候老是分不清防抖和节流。
  • 其实防抖和节流不仅仅在面试中会让大家手写,在实际项目中也可以起到性能优化的作用,所以还是很有必要掌握的。
  • 接下来我就用一杯茶的时间带大家彻底学会手写防抖和节流。

防抖

个例子

我们先抛开概念不谈,其实在生活中也有很多防抖的例子:

  • 比如你现在使用的电脑,在不使用后一段时间自动休眠
  • 当你再次使用的时候重新激活,并开始你设置的时间倒计时10分钟
  • 在这10分钟内你继续使用电脑又会重新开始倒计时10分钟
  • 当你最后一次使用电脑并离开时重新倒计时10分钟过去了,电脑就休眠了

这其实这就是防抖的基本概念了~说白了就是在一段时间只执行一次,也就是我们上面的电脑只打开这一次。

我们上面的使用电脑可以理解为触发事件,而与我上面标注的setTimeoutclearTimeout其实就是防抖的主要要素了。

当然上面的是生活中的例子,那我们在我们的日常开发中其实也经常用到,比如我们调整页面的大小,验证表单某个字段是否重复时发生请求次数控制,防止表单多次提交等。

手写防抖

说了这么多,相信大家大概理解了防抖的概念,那我们现在来实现一个防抖吧,假设我们要点击一个按钮新增一条信息,当然我们不希望每次点击都调用接口新增,我们希望多次点击只新增一次,这时候我们该怎么写呢?

首先我们先简单的模拟一个按钮被点击的过程。

// debounce.js
let addBtn=document.getElementById('add')
function addOne(){
  console.log('增加一个')
}
addBtn.addEventListener('click',addOne)

因为我们需要对执行的事件进行处理所以接下来我们需要封装一下addOne函数。

// debounce.js
let addBtn=document.getElementById('add')
function addOne(){
  console.log('增加一个')
}
function debounce(fun){
  return function(){
    fun()
  }
}
addBtn.addEventListener('click',debounce(addOne))

上面我们用闭包处理了一下addOne函数,接下来我们需要添加一个延时器setTimeout来倒计时,当我们点击按钮后过2s再执行。

// debounce.js
function debounce(fun,time){
  return function(){
    setTimeout(()=>{
      fun()
    },time)
  }
}
addBtn.addEventListener('click',debounce(addOne,2000))

现在延时的目的是达到了但是每次点击都会新增一个新的setTimeout而且并不能达到我们多次点击只执行一次的效果。

这时候就需要clearTimeout登场了,我们需要在我们点击了按钮后也就是debounce执行时要先把之前的setTimeout先清除再重新计时。

function debounce(fun,time){
  let timer
  return function(){
    clearTimeout(timer)
    timer=setTimeout(()=>{
      fun()
    },time)
  }
}

现在我们的一个防抖功能就完成了,但是这还没完,如果我们在addOne()打印this会发现我们这样执行的this是指向Window的。

这当然不是我们所希望的,我们需要使用apply来改变this指向,再者就是我们需要考虑到执行函数的参数,因为不同的函数肯定会有不同的参数传入,对于参数我们可以使用arguments处理。

function debounce(fun,time){
  let timer
  return function(){
    clearTimeout(timer)
    let args = arguments
    timer=setTimeout(()=>{
      fun.apply(this,args)
    },time)
  }
}

这样我们的防抖函数就手写完成了,看起来其实也并不难。

总而言之防抖就是在不断的操作中(输入、点击等)最终只执行一次的一种提高性能的方法

节流

举个例子

当然我们生活中也会有很多节流的的例子,不知道大家有没有留意过公园的洒水机:

  • 一般我们会给洒水机设定一个时间假设30min
  • 当距离上次洒水时间未够30min的时候一直保持静止状态
  • 而当到了30min则会触发洒水的事件

  • 这其实也是节流的最基本的概念了,说白了就是在间隔一段时间执行一次
  • 我们上面的洒水可以理解为触发事件,而我们上面标注的其他信息也就只有30min和当前时间,这两个就是节流的主要要素了。
  • 当然上面的是我们的生活中的例子,那我们在我们的日常开发中其实也经常用到,比如我们滚动鼠标滚轮监听滚动条位置,防止按钮多次点击等。

手写节流

说了这么多,相信大家大概理解了节流的概念,那我们现在来实现一个节流吧,假设我们现在要实现一个鼠标滚动打印事件,想让它在3s执行一次,这时候我们该怎么写呢?

首先我们先模拟一个触发事件。

// throttle.js
function scrollTest(){
  console.log('现在我触发了')
}
document.addEventListener('scroll',scrollTest)

接下来我们封装一个节流函数,跟防抖一样我们也需要利用闭包,顺便再加一个参数接收节流时间。

// throttle.js
...
function throttle(fun,time){
  return function(){
    fun()
  }
}
document.addEventListener('scroll',throttle(scrollTest,3000))

因为我们的节流是在一段时间内执行一次也就是说如果两次鼠标滚动的时间间隔未到所设置的时间则不执行

那我们可以记录一下每次滚动的时间戳来进行对比。

// throttle.js
...
function throttle(fun,time){
  let t1=0 //初始时间
  return function(){
    let t2=new Date() //当前时间
    if(t2-t1>time){
      fun()
      t1=t2
    }
  }
}

我们会记录两个时间一个是t1代表初始时间一个是t2代表当前时间,如果当前时间距离上一个时间也就是初始时间大于所设置的time

那我们就可以执行fun()并且把初始时间变更为这一次执行的时间,这样每次我们执行过后t1就变成了上一次执行的时间。

这样我们的一个节流功能就完成了。

// throttle.js
...
function throttle(fun,time){
  let t1=0 //初始时间
  return function(){
    let t2=new Date() //当前时间
    if(t2-t1>time){
      fun()
      t1=t2
    }
  }
}

当然我们也需要像防抖一样改变this指向和接收参数,最后完成后是这样的。

// throttle.js
...
function throttle(fun,time){
  let t1=0 //初始时间
  return function(){
    let t2=new Date() //当前时间
    if(t2-t1>time){
      fun.apply(this,arguments)
      t1=t2
    }
  }
}

至此一个节流函数就手写完成了,是不是也不会很难呢?

总而言之节流就是在一段时间内不断操作而在你规定的时间内只执行一次的一种提高性能的方法

写在最后

  • 首先还是很感谢大家看到这里,这次的文章就分享到这里~
  • 对于防抖和节流一个最主观的判断方法就是:在10s内你疯狂点击一个按钮,如果使用了防抖则会只执行一次,而你使用了节流则会每隔一段时间执行一次,这个时间可以自己来掌控。
  • 当然防抖和节流的变形还是有很多的,根据不同的需求来变换不同的函数但是万变不离其宗,只要你搞懂了上面的方法,其他的就没有问题了。
作者:快跑啊小卢_

%s 个评论

要回复文章请先登录注册