亲宝软件园·资讯

展开

一文学会JavaScript如何手写防抖节流

大眼睛图图 人气:0

前言

记得当初看这篇大佬的文章一杯茶的时间,带你彻底学会手写防抖节流已经对防抖节流有了个清晰的认识,但那个时候由于真的是第一次接触到防抖节流,对它的手写方式还是很迷,卡在着卡了很久。

所以今天打算在那篇文章的基础上做一些补充,让小白对防抖节流的手写能够真正掌握。

防抖节流的概念

一个经典的比喻:

想象每天上班大厦底下的电梯。把电梯完成一次运送,类比为一次函数的执行和响应

假设电梯有两种运行策略 debounce 和 throttle,超时设定为15秒,不考虑容量限制

电梯第一个人进来后,等待15秒。如果过程中又有人进来,15秒等待重新计时,直到15秒后开始运送,这是防抖。

电梯第一个人进来后,15秒后准时运送一次,这是节流。

再举个不太严谨的比喻:

防抖:就是王者荣耀里的回城,一段时间内被打断,就又要重新计时。

节流:就是王者荣耀里英雄的技能,像东皇的大一般都是20s准时来一次。

手写

防抖

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

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

加入防抖功能

// debounce.js
let addBtn=document.getElementById('add')

function addOne(){
  console.log('增加一个')
}

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

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

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

这时我们会很自然地想到这个写法:

function debounce(fun, time) {
    let timer;
    
     // 如果之前就存在定时器,就要把之前那个定时器删除
        if (timer) {
            clearTimeout(timer)
        }

        timer = setTimeout(() => {
            fun()
        }, time)
    
}

但是这样写的话拿的到之前定时器的值吗?

当然是拿不到的,因为每次都会重新let timer,所以这个时候我们就要使用闭包(延迟了变量的生命周期)了。

如果你还不太理解闭包,可以参考这篇文章包教包会——作用域链+闭包

引入闭包后

function addOne(){
  console.log('增加一个')
}

function debounce(fun, time) {
    let timer;
    
    return function(){
     // 如果之前就存在定时器,就要把之前那个定时器删除
        if (timer) {
            clearTimeout(timer)
        }

        timer = setTimeout(() => {
            fun()
        }, time)
        
    }
}

addBtn.addEventListener('click',debounce(addOne,2000))

这里还有一点需要知道的,每执行一次addEventListener,debounce因为返回的是一个函数,并结合addEventListener的特点,会直接执行debounce返回的函数,不会出现每次都let timer

function debounce(fun, time) {
    let timer;
    
    return function(){
     // 如果之前就存在定时器,就要把之前那个定时器删除
        if (timer) {
            clearTimeout(timer)
        }

        timer = setTimeout(() => {
            fun.apply(this, arguments)
        }, time)
        
    }
}

这样一个防抖就写出来了

节流

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

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.apply(this,arguments)
      t1=t2
    }
  }
}

应用场景

防抖在连续的事件,只需触发一次回调的场景有:

节流在间隔一段时间执行一次回调的场景有:

加载全部内容

相关教程
猜你喜欢
用户评论