关于javascript:跟着大佬学JavaScript之防抖

4次阅读

共计 4138 个字符,预计需要花费 11 分钟才能阅读完成。


theme: channing-cyan

highlight: ascetic

前言

在前端开发中会遇到一些频繁的事件触发,比方:

  1. window 的 resize、scroll
  2. mousedown、mousemove、mousewheel(鼠标滚轮)
  3. keyup(弹起键盘)、keydown(按下键盘)、keypress(按下字符键盘)
    ……

设想一下窗口的 resize 事件或者是一个元素的 onmouseover 事件 – 他们触发时,执行的十分迅速,并且触发很屡次。如果你的回调过重,你可能使浏览器死掉。

这就是为什么要应用防抖。

原理

防抖的原理:在 wait 工夫内,继续触发某个事件。第一种状况:如果某个事件触发 wait 秒内又触发了该事件,就应该以新的事件 wait 等待时间为准,wait 秒后再执行此事件;第二种状况:如果某个事件触发 wait 秒后,未再触发该事件,则在 wait 秒后间接执行该事件。

艰深一点:定义 wait=2000,继续点击按钮,前后点击距离都在 2 秒内,则在最初一次点击按钮后,期待 2 秒再执行 func 办法。如果点击完按钮,2 秒后未再次点击按钮,则 2 秒后间接执行 func 办法。

示例代码

代码一(依据原理)

定义函数 debounce
依据表述,咱们能够晓得须要传入参数:func、wait
实现代码:

function debounce(func, wait = 500) {
    let timeout; // 定义定时器,wait 秒后须要革除定时器
    return function () {
      // 如果再次触发函数时,已有 timeout,则清空销毁以后 timeout,再以新的事件从新设置定时器
      if (timeout) clearTimeout(timeout);
    
      timeout = setTimeout(function () {func();
        clearTimeout(timeout)
      }, wait);
    };
}

代码二(解决函数 this 指向)

咱们之前的原函数指向哪,如果应用咱们的 debounce 函数包裹后,也要将 this 指向正确的对象。

function debounce(func, wait = 500) {
    let timeout, context;
    return function () {
          context = this;
          // 如果再次触发函数时,已有 timeout,则清空销毁 timeout,在往下执行
          if (timeout) clearTimeout(timeout);
        
          timeout = setTimeout(function () {func.apply(context);
                clearTimeout(timeout);
          }, wait);
    };
}

代码三(解决函数 event 对象)

JavaScript 在事件处理函数中会提供事件对象 event;
因而,也要思考到放弃原函数的 event 对象雷同

式一:

function debounce(func, wait = 500) {
    let timeout, context, args;
    return function () {
          context = this;
          args = arguments;
          // 如果再次触发函数时,已有 timeout,则清空销毁 timeout,在往下执行
          if (timeout) clearTimeout(timeout);
        
          timeout = setTimeout(function () {func.apply(context, args);
                clearTimeout(timeout);
          }, wait);
    };
}

式二:

function debounce(func, wait = 500) {
    let timeout, context;
    return function (...args) {
          context = this;
          // 如果再次触发函数时,已有 timeout,则清空销毁 timeout,在往下执行
          if (timeout) clearTimeout(timeout);
        
          timeout = setTimeout(function () {func.apply(context, args);
                clearTimeout(timeout);
          }, wait);
    };
}

代码四(函数返回值)

此时须要留神一个问题,就是咱们在执行原函数时可能有返回值,咱们须要解决 debounce 函数,在最初也要有雷同返回值。

这里做出的解决,是将 func.apply(context, args) 独自拿进去,输入原函数的result

function debounce(func, wait = 500) {
    let timeout, context, result;
    
    function showResult(e1, e2) {result = func.apply(e1, e2); // 绑定 e1,e2 的同时,输入 result
        return result;
    }
    
    return function (...args) {
        context = this;
        // 如果再次触发函数时,已有 timeout,则清空销毁 timeout,在往下执行
        if (timeout) clearTimeout(timeout);
        
        // 这里是不立刻执行的原代码
        timeout = setTimeout(function () {showResult(context, args); // 将 this,arguments 代入函数
            clearTimeout(timeout);
        }, wait);
    };
}

代码五(立即执行)

因为原理中,每次触发完后还须要期待 wait 秒执行。
然而某些场景,比方按钮点击后调用接口,会使整个工夫变长,这时候就须要定义 immediate,点击按钮,立刻执行调用接口,还要达到 wait 秒内防抖的成果。

function debounce(func, wait = 500, immediate = false) {
    let timeout, context, result, callNow;
    
    function showResult(e1, e2) {result = func.apply(e1, e2); // 绑定 e1,e2 的同时,输入 result
        return result;
    }
    
    return function (...args) {
        context = this;
        // 如果再次触发函数时,已有 timeout,则清空销毁 timeout,在往下执行
        if (timeout) clearTimeout(timeout);
        if (immediate) {
            // 这里是立刻执行的判断代码
            callNow = !timeout; // timeout 最开始定义为 undefined,如果未设置定时器,则!timeout 返回 true; 否则返回 false
            timeout = setTimeout(function () {timeout = null; // 这里时定时器走完,让 timeout 为 null,则上一步!timeout 仍然返回 true;}, wait);
            if (callNow) showResult(context, args); // 刚进入 timeout=undefined 以及,wait 工夫走完 timeout = null,两种状况都会立刻执行函数
        } else {
        // 这里是不立刻执行的原代码
            timeout = setTimeout(function () {showResult(context, args); // 将 this,arguments 代入函数
                clearTimeout(timeout);
            }, wait);
        }
    };
}

代码六(勾销)

减少勾销防抖的办法:只须要定义 cancel 办法,去除定时器,将初始变量全副设置为 undefined。

function debounce(func, wait = 500, immediate = false) {
    let timeout, context, result, callNow;
    
    function showResult(e1, e2) {result = func.apply(e1, e2); // 绑定 e1,e2 的同时,输入 result
        return result;
    }
    
    const debounced = function (...args) {
        context = this;
        // 如果再次触发函数时,已有 timeout,则清空销毁 timeout,在往下执行
        if (timeout) clearTimeout(timeout);
        if (immediate) {
            // 这里是立刻执行的判断代码
            callNow = !timeout; // timeout 最开始定义为 undefined,如果未设置定时器,则!timeout 返回 true; 否则返回 false
            timeout = setTimeout(function () {timeout = null; // 这里时定时器走完,让 timeout 为 null,则上一步!timeout 仍然返回 true;}, wait);
            if (callNow) showResult(context, args); // 刚进入 timeout=undefined 以及,wait 工夫走完 timeout = null,两种状况都会立刻执行函数
        } else {
            // 这里是不立刻执行的原代码
                timeout = setTimeout(function () {showResult(context, args); // 将 this,arguments 代入函数
                    clearTimeout(timeout);
                }, wait);
        }
    };
    
    debounced.cancel = function () {
        // 去除定时器,if (timeout !== undefined) {clearTimeout(timeout);
        }
        // 将初始变量全副设置为 undefined
        timeout = context = result = callNow = undefined;
    };
    return debounced;
}

演示地址

能够去 Github 仓库查看演示代码

跟着大佬学系列

次要是日常对每个进阶知识点的摸透,跟着大佬一起去深刻理解 JavaScript 的语言艺术。

后续会始终更新,心愿各位看官不要悭吝手中的赞。

❤️ 感激各位的反对!!!

❤️ 如果有谬误或者不谨严的中央,请务必给予斧正,非常感激!!!

❤️ 喜爱或者有所启发,欢送 star!!!

参考

  • JavaScript 专题之跟着 underscore 学防抖
  • underscore.js
  • 深入浅出防抖函数 debounce

原文地址

【跟着大佬学 JavaScript】之防抖

正文完
 0