手写防抖和节流

作为性能优化的手段,防抖和节流在前端界颇有名声,虽然网上的代码看起来封装得很复杂,但是经过一层层分解,可以知道这就是很基础的代码技巧,现在我来从最基础的代码模拟实现一遍防抖和节流。

防抖 debounce

思路

防抖的概念就是使用清除定时器来对用户的操作进行限制,相当于一个 cd 条,如果打断了,就不执行(清除定时器)。

比如我现在要做一个 button 按钮,点击两秒钟后打出一句 123,说干就干。

javascript
1let btn=document.querySelector('button')
2btn.addEventListener('click',()=>{
3  setTimeout(()=>{console.log(123)},1000)
4})

没有防抖前效果如下 简单做个防抖

javascript
1let btn=document.querySelector('button')
2let timer=null
3btn.addEventListener('click',()=>{
4  clearTimeout(timer) //每点一次都清除上一次的timer,上一次的就不会执行
5  timer=setTimeout(()=>{console.log(123)},1000)
6  console.log(timer)
7  //这句代码是为了证明获取timer比console早
8  //只要执行了setTimeout就拿到timer了,不用执行参数函数console
9})

效果如下:

优化代码

javascript
1let btn=document.querySelector('button')
2
3function debounce(callback, delay) {
4  let timer;
5  return function (...rest) {
6    clearTimeout(timer);
7    const context = this;
8    timer = setTimeout(() => {
9      callback.apply(context, rest);
10      clearTimeout(timer);
11    }, delay);
12  };
13}
14
15const handler=()=>{console.log(123)}
16btn.addEventListener('click',debounce(handler,1000))

优化代码思路:

逻辑是基于上面的简化版进行的函数封装,就是使用闭包,把 timer 包起来。

然后按照里面的内容拆开写debounce的参数,以上的参数是处理函数 handler(就是 console.log(123))和 delay(时间),如果有更多的业务逻辑,就写更多的参数做封装就行了。

节流 throttle

后来发现节流实际上就是不管用户点多少次,我只执行第一次。

节流与防抖的区别

它跟防抖的关系好比一个法师在施法,如果这个法师不断在施法,但是它的技能是可以被打断的,每打断一次都重新触发施法那就是防抖。

如果这个法师的技能不能被打断,不管你打了多少次,法师都能把刚开始的施法读条 delay 给做完,这就是节流。

示例

没有节流的效果

思路:如何让上面的代码在我设置的时间范围内只跑一次呢?还是使用 setTimeout 实现,如果有一个开关,当开关是打开的时候,就跑代码。如果开关闭合,就不执行代码,跑代码过程中将闭合开关不就行了吗?

节流的效果 代码如下

javascript
1let btn=document.querySelector('button')
2const handler=()=>{console.log('初步节流')}
3let toggle=true //设置一个开关
4btn.addEventListener('click',()=>{
5  if(!toggle){//如果开关是关着的,就return
6    return null
7  }
8  toggle=false //此时函数正在执行,把开关闭合
9  setTimeout(()=>{
10    handler() //这里是处理业务逻辑了
11    toggle=true //处理完之后把开关打开
12  },1000)
13})

闭包封装

javascript
1let btn=document.querySelector('button')
2const handler=()=>{console.log('闭包封装')}
3function throttle(){
4  let toggle=true //把toggle当做闭包
5  return function (){
6    if(!toggle){
7      return null
8    }
9    toggle=false
10    setTimeout(()=>{
11      toggle=true
12      handler()
13    },1000)
14  }
15}
16btn.addEventListener('click',throttle())

优化代码

javascript
1let btn=document.querySelector('button')
2const handler=()=>{console.log('封装函数')}
3
4/*
5参数:
6handler: 执行函数
7delay:时间
8*/
9function throttle(callback, delay) {
10  let timer;
11  let flag = false;
12  return function (...rest) {
13    if (flag) {
14      return;
15    }
16    flag = true;
17    const context = this;
18    timer = setTimeout(() => {
19      callback.apply(context, rest);
20      clearTimeout(timer);
21      flag = false;
22    }, delay);
23  };
24}
25btn.addEventListener('click',throttle(handler,2000))

结束~

enjoy!