源码学习——防抖之underscore的实现

80 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天,点击查看活动详情

本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。

这是源码共读的第2期,链接:juejin.cn/post/708744…

什么是防抖

在触发事件n秒后才执行,也就是说,尽管触发事件,在事件触发的n秒内又触发了这个事件,就重新计算,以这次触发事件的时间为准,n秒后再执行。也就是,等你触发完事件n秒内不再触发事件,才执行。

防抖的作用

日常开发中,防抖主要用在页面事件点击上,比如请求事件,或跳转页面,如果不做防抖的话,用户频繁触发的话,就会在短时间多次执行相关的事件,导致异常的卡顿,或无意义的多次请求,多次跳转。

如何实现防抖

根据上面的作用,我们可以考虑下如何实现防抖:

  • 在触发事件之后n秒执行,则需要依赖setTimeout来实现。
  • 涉及到事件等异步方式,则this指向也是需要考虑的。
  • 同时还需要考虑事件event对象的传递
  • 最后就是要考虑如何取消防抖

来看下代码实现:

 function debounce(fn, delay) {
     var _debounce = function (args) {
        let that = this;
        let _args = args;
        clearTimeout(fn.id);
        fn.id = setTimeout(function () {
            fn.apply(that, _args);
        }, delay)
    }
    _debounce.cancel = function(){
        clearTimeout(fn.id);
        fn.id = null;
    }
    return _debounce;
}

underscore中防抖的实现

源码地址:github.com/jashkenas/u…

import restArguments from './restArguments.js';
import now from './now.js';

// When a sequence of calls of the returned function ends, the argument
// function is triggered. The end of a sequence is defined by the `wait`
// parameter. If `immediate` is passed, the argument function will be
// triggered at the beginning of the sequence instead of at the end.
export default function debounce(func, wait, immediate) {
  var timeout, previous, args, result, context;

  var later = function() {
    var passed = now() - previous;
    if (wait > passed) {
      timeout = setTimeout(later, wait - passed);
    } else {
      timeout = null;
      if (!immediate) result = func.apply(context, args);
      // This check is needed because `func` can recursively invoke `debounced`.
      if (!timeout) args = context = null;
    }
  };

  var debounced = restArguments(function(_args) {
    context = this;
    args = _args;
    previous = now();
    if (!timeout) {
      timeout = setTimeout(later, wait);
      if (immediate) result = func.apply(context, args);
    }
    return result;
  });

  debounced.cancel = function() {
    clearTimeout(timeout);
    timeout = args = context = null;
  };

  return debounced;
}

underscore中的实现,作为开源项目,比我们自己实现的要严谨很多,健壮很多,这就是我们学习开源库的目的,学习开源项目在写一些工具函数时,都会考虑哪些?比如:函数的抽象、参数的校验、边界条件的判断等等,都是需要我们特别要注意的,这些在日常开发中是比较容易忽略的,大部分都是以满足现有需求为主。

需要注意的是,underscore的实现中多了立即执行的能力。

  • immediate该参数表示,不需要等待,直接执行,以满足一些特殊场景的使用。

总结

防抖、节流是我们必须要掌握的,任何一个前端业务项目都会涉及到这块,只要有点击事件,或滚动事件都会用到。大部分时候我们都是引用工具库的,或者自己内部项目的工具库,因此了解其原理及应用场景是一个前端开发必备的知识点。