美文网首页深入JavaScriptJavaScript收藏
深入JavaScript Day32 - 手写防抖函数debou

深入JavaScript Day32 - 手写防抖函数debou

作者: 望穿秋水小作坊 | 来源:发表于2022-02-17 14:56 被阅读0次

    一、防抖函数代码实现

    防抖:适用于高频函数的【延迟执行】,比如搜索框的联想功能

    0、测试代码

    <!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>Document</title>
      </head>
      <body>
        <input type="text" /> <button>取消</button>
    
        <script src="https://cdn.jsdelivr.net/npm/underscore@1.13.2/underscore-umd-min.js"></script>
        <script src="./04_debounce-cancel.js"></script>
        <script>
          const inputEl = document.querySelector("input");
          let counter = 0;
          const handleInputChange = function (event) {
            console.log(`函数被调用了 ${++counter} 次`, this, event);
            return "aaaaa";
          };
          // const debounceFunc = _.debounce(handleInputChange, 2000);
          const debounceFunc = debounce(handleInputChange, 3000, true, (res) => {
            console.log("callback:", res);
          });
    
          inputEl.oninput = function (...arg) {
            const promise = debounceFunc.apply(this, arg);
            promise
              .then((res) => {
                console.log("promise:", res);
              })
              .catch((err) => {
                console.log(err);
              });
          };
    
          const buttonEl = document.querySelector("button");
          buttonEl.onclick = function (event) {
            debounceFunc.cancel();
          };
        </script>
      </body>
    </html>
    

    1、最基本的防抖函数实现【版本一】

    function debounce(fn, delay) {
      // 1.定义一个定时器, 保存上一次的定时器
      let timer = null
    
      // 2.真正执行的函数
      const _debounce = function() {
        // 取消上一次的定时器
        if (timer) clearTimeout(timer)
        // 延迟执行
        timer = setTimeout(() => {
          // 外部传入的真正要执行的函数
          fn()
        }, delay)
      }
    
      return _debounce
    }
    
    

    2、解决 func内部使用this和event都会丢失【版本二】

    function debounce(fn, delay) {
      // 1.定义一个定时器, 保存上一次的定时器
      let timer = null
    
      // 2.真正执行的函数
      const _debounce = function(...args) {
        // 取消上一次的定时器
        if (timer) clearTimeout(timer)
        // 延迟执行
        timer = setTimeout(() => {
          // 外部传入的真正要执行的函数
          fn.apply(this, args)
        }, delay)
      }
    
      return _debounce
    }
    

    3、增加需求,防抖函数首次立即执行【版本三】

    function debounce(fn, delay, immediate = false) {
      // 1.定义一个定时器, 保存上一次的定时器
      let timer = null
      let isInvoke = false
    
      // 2.真正执行的函数
      const _debounce = function(...args) {
        // 取消上一次的定时器
        if (timer) clearTimeout(timer)
    
        // 判断是否需要立即执行
        if (immediate && !isInvoke) {
          fn.apply(this, args)
          isInvoke = true
        } else {
          // 延迟执行
          timer = setTimeout(() => {
            // 外部传入的真正要执行的函数
            fn.apply(this, args)
            isInvoke = false
          }, delay)
        }
      }
    
      return _debounce
    }
    

    4、增加需求,给防抖函数增加一个取消功能【最终版本四】

    function debounce(fn, delay, immediate = false) {
      // 1.定义一个定时器, 保存上一次的定时器
      let timer = null
      let isInvoke = false
    
      // 2.真正执行的函数
      const _debounce = function(...args) {
        // 取消上一次的定时器
        if (timer) clearTimeout(timer)
    
        // 判断是否需要立即执行
        if (immediate && !isInvoke) {
          fn.apply(this, args)
          isInvoke = true
        } else {
          // 延迟执行
          timer = setTimeout(() => {
            // 外部传入的真正要执行的函数
            fn.apply(this, args)
            isInvoke = false
            timer = null
          }, delay)
        }
      }
    
      // 封装取消功能
      _debounce.cancel = function() {
        if (timer) clearTimeout(timer)
        timer = null
        isInvoke = false
      }
    
      return _debounce
    }
    

    5、增加需求,给防抖函数增加获取返回值功能【特殊版本五】,有哪两种拿返回值的思路?

      // 1.定义一个定时器, 保存上一次的定时器
      let timer = null
      let isInvoke = false
    
      // 2.真正执行的函数
      const _debounce = function(...args) {
        return new Promise((resolve, reject) => {
          // 取消上一次的定时器
          if (timer) clearTimeout(timer)
    
          // 判断是否需要立即执行
          if (immediate && !isInvoke) {
            const result = fn.apply(this, args)
            if (resultCallback) resultCallback(result)
            resolve(result)
            isInvoke = true
          } else {
            // 延迟执行
            timer = setTimeout(() => {
              // 外部传入的真正要执行的函数
              const result = fn.apply(this, args)
              if (resultCallback) resultCallback(result)
              resolve(result)
              isInvoke = false
              timer = null
            }, delay)
          }
        })
      }
    
      // 封装取消功能
      _debounce.cancel = function() {
        if (timer) clearTimeout(timer)
        timer = null
        isInvoke = false
      }
    
      return _debounce
    }
    

    二、节流函数代码实现

    节流:适用于高频函数的【分时间段执行】,比如飞机大战里面的按键发射子弹功能

    0、测试html代码

    <!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>Document</title>
      </head>
      <body>
        <button>发射</button>
    
        <script src="https://cdn.jsdelivr.net/npm/underscore@1.13.2/underscore-umd-min.js"></script>
        <script src="./02_节流首次执行可配.js"></script>
        <script>
          const buttonEl = document.querySelector("button");
          let counter = 0;
          const handleShot = function (event) {
            console.log(`子弹发射${++counter}次`, this, event);
          };
          const throttleFunc = throttle(handleShot, 2000, {
            leading: false,
            trailing: true,
          });
          buttonEl.onclick = throttleFunc;
        </script>
      </body>
    </html>
    
    

    1、节流函数最简版实现【版本一】

    function throttle(func, interval) {
      let lastTime = 0;
      const throttleFunc = function () {
        let currentTime = new Date().getTime();
        let remainTime = interval - (currentTime - lastTime);
        if (remainTime < 0) {
          func();
          lastTime = currentTime;
        }
      };
      return throttleFunc;
    }
    

    2、节流函数首次执行可配置【版本二,一般用此版本即可】

    function throttle(func, interval, options = { leading: true }) {
      let { leading } = options;
      let lastTime = 0;
      const throttleFunc = function (...args) {
        if (!leading && lastTime === 0) {
          lastTime = new Date().getTime();
        }
        let currentTime = new Date().getTime();
        let remainTime = interval - (currentTime - lastTime);
        if (remainTime < 0) {
          func.apply(this, args);
          lastTime = currentTime;
        }
      };
      return throttleFunc;
    }
    

    3、节流函数末次执行可配置【版本三】,取消功能和返回值功能参照防抖函数实现。

    function throttle(
      func,
      interval,
      options = { leading: true, trailing: false }
    ) {
      let { leading, trailing } = options;
      let lastTime = 0;
      let timer = null;
      const throttleFunc = function () {
        if (!leading && lastTime === 0) {
          lastTime = new Date().getTime();
        }
        let currentTime = new Date().getTime();
        let remainTime = interval - (currentTime - lastTime);
        if (timer) clearTimeout(timer);
        if (remainTime <= 0) {
          func();
          lastTime = currentTime;
        } else if (trailing) {
          timer = setTimeout(() => {
            func();
            lastTime = new Date().getTime();
          }, remainTime);
        }
      };
      return throttleFunc;
    }
    

    相关文章

      网友评论

        本文标题:深入JavaScript Day32 - 手写防抖函数debou

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