美文网首页
JavaScript 之函数防抖

JavaScript 之函数防抖

作者: 临安linan | 来源:发表于2019-11-08 20:01 被阅读0次

    更多个人博客:(https://github.com/zenglinan/blog)

    如果对你有帮助,欢迎star。

    防抖的原理: 为了避免事件频繁触发执行, 让事件只有在触发后 n 秒才执行, 假如 n 秒内重新触发了事件, 则刷新时间戳, 在下一个 n 秒后执行
    使用方式大概是: dom.onmousemove = debounce(getUserAction, 1000);

    基础版防抖: 输入指定时间, 实现防抖

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

    只要在 time 内重新触发了事件, 定时器就会被清除掉, 重新开启下一个。

    改良版

    上一版实现了基础代码, 但是还有些地方需要完善, 比如:

    1. this 指向被改变了, 需要保证 fn 内部的 this 指向触发元素本身
    2. 需要提供 event 对象

      这里为了保证 this, 采用箭头函数, 同时将 arguments 参数对象传入, 保证 event 对象可以获取到
    function debounce(fn, time){
      let timer
      return function(){
        // 事件触发时这里的 this 指向的是元素
        let args = arguments  // 取出的参数对象里包含了 event 对象
        clearTimeout(timer)
        timer = setTimeout(() => {
          fn.apply(this, args)
        }, time)
      }
    }
    

    立即执行

    以上, 我们的防抖函数已经比较完备了

    但是还可以添加一个需求使之更完善:

    不要等到事件停止触发后才执行,我希望立刻执行函数,然后等到停止触发 n 秒后,才可以重新触发执行

    这里用一个可选参数来决定是否要采取这种立即触发的模式

    function debounce(fn, time, immediate){
      let timer
      return function(){
        let args = arguments
        clearTimeout(timer)
    
        if(immediate){  // 采取立即执行模式
          let callNow = !timer  // 只有 timer 为 falsy 值时, 才立即执行
          timer = setTimeout(()=>{
            timer = null  // 只要 time 时间不触发, 就把 timer 设为 null, 那么下一次就会直接执行了
          }, time)
          callNow && fn.apply(this, args)
        }
        
        else{  // 采取传统模式
          timer = setTimeout(() => {
            fn.apply(this, args)
          }, time)
        }
      }
    }
    

    思路剖析: 当传入第三个参数为 true 时, 开启立即执行模式, 第一次的时候 timer 肯定为 undefined, 这时候 callNow 为 true, 立即执行

    与此同时, 给 timer 赋值定时器, 这时候 timer 就不是 falsy 值了, 如果马上再触发一次, !timer 为false, 不会马上执行

    同时, timer 设置的定时器里, time 时间后就会把 timer 重置为 null, 但是每次触发都会清除这个定时器, 所以只要在 time 时间内不去触发, 再下次触发时, !timer 就会为 true, 就能立即执行了

    要注意: 定时器返回给 timer 的是数字, 即使清除了定时器, timer 的值也不会被清除, 只有手动赋值才能让其为 falsy 值。

    可取消

    这里还可以再完善一下, 添加重置 debounce 函数的功能,

    假如说 debounce 的时间间隔是 10 s, immediate 为 true, 我只有等 10 秒后才能重新触发事件

    我希望有时能够立即重置防抖, 即回到立即执行的状态。

    只需添加一个取消函数来重置 timer 即可

    function debounce(fn, time, immediate){
      let timer
      var debounceFn = function(){
        let args = arguments
        clearTimeout(timer)
    
        if(immediate){
          let callNow = !timer
          timer = setTimeout(()=>{
            timer = null
          }, time)
          callNow && fn.apply(this, args)
        }
        
        else{
          timer = setTimeout(() => {
            fn.apply(this, args)
          }, time)
        }
      }
      debounceFn.reset = function(){  // 取消函数, 注意: 取消完后又恢复
        clearTimeout(timer)
        timer = null
      }
      return debounceFn
    }
    

    使用的时候需要这样使用

    let debounced = debounce(fn, 1000)
    dom.onmousemove = debounced
    // 重置函数的使用方法
    button.onclick = debounced.reset
    

    参考: https://github.com/mqyqingfeng/Blog/issues/22

    相关文章

      网友评论

          本文标题:JavaScript 之函数防抖

          本文链接:https://www.haomeiwen.com/subject/wciwbctx.html