防抖和节流(实例讲解)

防抖和节流到底是什么?

防抖和节流属于性能优化的知识,它可以有效的降低高频事件触发时,你定义的方法的执行次数。

还是没有感觉???那么,来看下面的场景:

  1. 用户在搜索框输入关键词(只有当他输入完成时我们才去向服务器发送请求,然后给出搜索结果)
  2. 自动保存用户填写的表单数据

上面的场景都对应着一个高频事件,即input或者textarea的onKeyUp事件,我们一般是在用户触发这个事件后去向服务器发送请求(这样做的好处是不需要用户去点击搜索按钮,有一种实时查询的感觉)。

那么问题来了,当用户输入一个要查询的关键词,可能需要多次按下和抬起键盘的按键,难道每次onKeyUp的时候我们都要去请求服务器吗?显然不够优雅(因为如果有大量用户同时搜索,服务器压力会很大)。而 防抖(debounce) 正是要解决类似这样的问题。

在浏览器中我们经常会遇到类似的事件(如浏览器scroll,resize,mousemove…)接下来,我们使用 自动保存 的场景来说明一下在 JavaScript 中如何实现防抖。

场景描述:用户在textarea中输入文字后,要为他自动保存到服务器(可以理解为保存为草稿)这时我们需要做的是 优化 请求服务器的次数,需要用到防抖函数。

防抖函数

先来看一个常见的错误写法,注意!!!百度中搜出的很多结果都是这个样子,用了之后就会发现,你的函数还是会立刻执行,并不会延时执行。

function debounce(fn, delay) {
    let timer = null
    return function (args) {
        if (timer) {
            clearTimeout(timer) 
        }
        timer = setTimeout(fn.call(this, args), delay)
    }
}

问题出在 timer = setTimeout(fn.call(this, args), delay) 这一行。

修改成下面的样子,就可以按设定的delay延时执行了:

function debounce(fn, delay) {
    let timer = null
    return function (args) {
        if (timer) {
            clearTimeout(timer) 
        }
        timer = setTimeout(function() {
            fn.call(this, args)
        }, delay)
    }
}

// 或者
function debounce(fn, delay) {
  let timer = null
    return function (args) {
      if (timer) {
        clearTimeout(timer)
      }
      timer = setTimeout(function() {
        fn(args)
      }, delay)
    }
}

不要小看这小小的区别,它可能会浪费你大量的时间,而且让你对防抖产生怀疑…

下面贴一个完整的例子,还有 防抖在线演示地址,方便你更好的理解这个场景。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>防抖和节流</title>
    <style>
        .de_wrapper {
          padding: 20px;
          display: flex;
        }
        .col {
        	width: 40%;
        }
        .log {
          height: 300px;
          overflow-y: scroll;
          background-color: #fff;
        }
    </style>
</head>
<body>
<div class="de_wrapper">
    <div class="col">
        <h3>未使用防抖(每次按键抬起都会触发保存)</h3>
        <textarea name="" id="1" cols="30" rows="10" onKeyUp="printLog(event)"></textarea>
        <div id="log" class="log"></div>
    </div>
    <div class="col">
        <h3>使用防抖(停止输入2秒后保存)</h3>
        <!-- <textarea name="" id="2" cols="30" rows="10"></textarea> -->
        <textarea name="" id="2" cols="30" rows="10" onKeyUp="debounceLog(event)"></textarea>
        <div id="log1" class="log"></div>
    </div>
</div>
<script>
    let log = null
    let log1 = null

    window.onload = function() {
        log = document.getElementById('log')
        log1 = document.getElementById('log1')

        // 写法1
        // document.getElementById('2').addEventListener('keyup', function(e) {
        //     debounceLog(e)
        // })

        // 写法2
        // document.getElementById('2').addEventListener('keyup', debounceLog)

        // 写法3
        // document.getElementById('2').addEventListener('keyup', debounce(printDebounceLog, 2000))

    } 

    function printLog(e) {
        log.innerText += `keyup 事件触发【请求服务器保存数据...】: ${e.target.value}\n`
    }

    function printDebounceLog(e) {
        log1.innerText += `keyup 事件触发【请求服务器保存数据...】: ${e.target.value}\n`
    }

    let debounceLog = debounce(printDebounceLog, 2000)

    function debounce(fn, delay) {
      let timer = null
        return function (args) {
          if (timer) {
            clearTimeout(timer)
          }
          timer = setTimeout(function() {
            fn(args)
          }, delay)
        }
    }
</script>
</body>
</html>

节流函数

节流函数(throttle)与防抖函数的区别:函数节流无论事件触发多么频繁,在一定时间内只会执行一次回调;而函数防抖是在高频事件的最后一次触发回调。

节流函数使用场景:一个很形象的例子就是mousedown发射子弹,每秒只能发出一颗子弹,在线演示地址

function throttle(fn, limit) {
  let lastTime
  return function(args) {
    if (!lastTime) {
      fn.apply(this. args)
      lastTime = Date.now()
    } else {
      if ((Date.now() - lastTime) >= limit) {
        fn.apply(this. args)
        lastTime = Date.now()
      }
    }
  }
}
// 页面结构
<div class="de_wrapper">
    <div class="col">
        <h3>未使用节流(点击按钮可以疯狂发射子弹)</h3>
        <div class="sky"></div>
        <button class="fire_btn">发射</button>
      </div>
      <div class="col">
        <h3>使用节流(发射子弹速度会被限制)</h3>
        <div class="sky"></div>
        <button class="fire_btn">发射</button>
    </div>
</div>

<script>

let sky = null
let sky1 = null
let btn = null
let btn1 = null

window.onload = function() { 
    sky = document.querySelectorAll('.sky')[0]
    sky1 = document.querySelectorAll('.sky')[1]
    btn = document.querySelectorAll('.fire_btn')[0]
    btn1 = document.querySelectorAll('.fire_btn')[1]

    btn.addEventListener('click', fire)
    btn1.addEventListener('click', throttle(t_fire, 1000))
} 

function fire() {
  const b = document.createElement('span')
  b.classList.add('bullet')
  sky.appendChild(b)
  setTimeout(() => {
    sky.removeChild(b)
  }, 1000)
}

function t_fire() {
  const b = document.createElement('span')
  b.classList.add('bullet')
  sky1.appendChild(b)
  setTimeout(() => {
    sky1.removeChild(b)
  }, 1000)
}

function throttle(fn, limit) {
  let lastTime
  return function(args) {
    if (!lastTime) {
      fn.apply(this. args)
      lastTime = Date.now()
    } else {
      if ((Date.now() - lastTime) >= limit) {
        fn.apply(this. args)
        lastTime = Date.now()
      }
    }
  }
}

总结

  1. 函数防抖:将多次操作合并为一次操作进行,原理是维护一个计时器,后设置的定时器会取代之前的定时器,如果高频事件一直在触发那么回调函数一直不会执行。
  2. 函数节流:使得一定时间内只触发一次函数。原理是通过判断是否满足限制时间,满足则执行。

文章首发于 《IICOOM-个人博客 防抖和节流》

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
防抖函数节流函数都是用来限制函数的执行频率,防止短时间内多次触发导致性能问题。 防抖函数(Debounce)的作用是将多次连续触发的函数调用合并为一个函数调用。当事件触发后,设定一个定时器,在指定的时间间隔内如果事件再次触发,则重新计时,直到指定时间间隔内没有事件再次触发,才执行函数。 下面是一个使用防抖函数的例子: ```javascript function debounce(func, delay) { let timer = null; return function(...args) { clearTimeout(timer); timer = setTimeout(() => { func.apply(this, args); }, delay); }; } // 使用防抖函数来限制按钮点击事件的频率 const button = document.querySelector('button'); button.addEventListener('click', debounce(() => { console.log('Button clicked'); }, 2000)); ``` 节流函数(Throttle)的作用是在一定时间间隔内只执行一次函数。当事件触发后,设定一个定时器,在指定的时间间隔内只能执行一次函数。 下面是一个使用节流函数的例子: ```javascript function throttle(func, delay) { let timer = null; let lastTime = 0; return function(...args) { const currentTime = new Date().getTime(); if (currentTime - lastTime > delay) { func.apply(this, args); lastTime = currentTime; } }; } // 使用节流函数来限制滚动事件的触发频率 window.addEventListener('scroll', throttle(() => { console.log('Scrolling'); }, 1000)); ``` 这样,在按钮点击事件或者滚动事件触发时,就可以控制其执行频率,避免频繁触发导致性能问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值