在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。

  1. <button>剁手按钮</button>
  2. <script>
  3. const button = document.querySelector('button')
  4. const payMoney = function () {
  5. console.log('剁手成功');
  6. console.log(this);
  7. }
  8. function dobounce(fun, time) {
  9. let timer
  10. return function () {
  11. const that = this
  12. clearTimeout(timer)
  13. timer = setTimeout(() => {
  14. fun.call(that)
  15. }, time);
  16. }
  17. }
  18. button.addEventListener('click', dobounce(payMoney, 1000))
  19. </script>

回调函数首次不自动执行

如果在button的监听事件中直接调用payMoney函数,则会发生还没有点击按钮函数就已经执行的情况,所以这里需要用到高阶函数,定义一个dobounce函数将payMoney函数return出来,这样就实现了不点击按钮函数就不执行的效果。

清除延时

要清除延时,我们就需要将整个setTimeout赋值给一个变量,这里我设置为timer,很显然,想要清除timer,就必须先定义timer,因此会有萌新写成这样:

  1. function dobounce(fun, time) {
  2. return function () {
  3. let timer
  4. const that = this
  5. console.log('已点击');
  6. clearTimeout(timer)
  7. timer = setTimeout(() => {
  8. fun.call(that)
  9. }, time);
  10. }
  11. };

这样表面上达到了setTimeout执行之前清除延时的效果,但实际效果是,延时结束后,延时期间的所有点击都生效了, 这是因为每次点击执行函数之间都是独立的,每次点击都定义了一个新的timer,它们之间没有联系 。要解决这个问题其实也简单,我们把timer的定义提到外层,利用闭包的特性,这样每次点击执行的函数访问到的都是同一个timer

this指向

表面上来看,防抖函数到这里就已经实现了,看不出有啥问题,但是如果我们打印payMoney函数中的this,会发现它指向window而不是button,这就是this丢失了,虽然表面上看起来没问题,但在实际开发中却会产生令人头疼又难以发现的bug,所以我们需要绑定this。我在setTimeout之前定义了一个that将this保存了起来,在setTimeout中使用call(that)实现绑定this的效果。到这里防抖函数才算完全实现了。

案例分析

  1. let biu = function () {
  2. console.log('biu biu biu',new Date().Format('HH:mm:ss'))
  3. }
  4. let boom = function () {
  5. console.log('boom boom boom',new Date().Format('HH:mm:ss'))
  6. }
  7. setInterval(debounce(biu,500),1000)
  8. setInterval(debounce(boom,2000),1000)

165a252b4b809a23_tplv-t2oaga2asx-zoom-in-crop-mark_1304_0_0_0 (1).gif

这个示例就很好的解释了,如果在时间间隔内执行函数,会重新触发计时。biu会在第一次1.5s执行后,每隔1s执行一次,而boom一次也不会执行。因为它的时间间隔是2s,而执行时间是1s,所以每次都会重新触发计时

函数节流(throttle)

规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。

  1. <input type="text" id="throttle">
  1. function throttle(fun, delay) {
  2. let last, deferTimer
  3. return function (args) {
  4. let that = this
  5. let _args = arguments
  6. let now = +new Date()
  7. if (last && now < last + delay) {
  8. clearTimeout(deferTimer)
  9. deferTimer = setTimeout(function () {
  10. last = now
  11. fun.apply(that, _args)
  12. }, delay)
  13. }else {
  14. last = now
  15. fun.apply(that,_args)
  16. }
  17. }
  18. }
  19. let inputc = document.getElementById('throttle');
  20. let throttleAjax = throttle(updateInp, 1000);
  21. function updateInp(e){
  22. console.log(e);
  23. }
  24. inputc.addEventListener('keyup', function(e) {
  25. throttleAjax(e.target.value)
  26. });
  • 可以看到,我们在不断输入时,updateInp会按照我们设定的时间,每1s执行一次。

    案例分析

    ```json let biubiu = function () { console.log(‘biu biu biu’, new Date()) };

setInterval(throttle(biubiu,1000),10) ``` 165a252b46818296_tplv-t2oaga2asx-zoom-in-crop-mark_1304_0_0_0.gif

不管我们时间设置的多小 ,总是1s内只执行一次。

总结

  • 函数防抖和函数节流都是防止某一时间频繁触发,但是这两兄弟之间的原理却不一样。
  • 函数防抖是某一段时间内只执行一次,而函数节流是间隔时间执行。