JS防抖函数debounce,underscore的防抖代码分析

防抖大致意思是高频触发的函数,在一定的时间间隔内只会执行一次。主要用来处理窗口的resize,滑动,滚动,输入等高频事件,也可以处理防止恶意高频点击按钮。

简易版防抖

	const d = function(fn, wait){
        let timeout = null
        return function(){
          clearTimeout(timeout)
          let args = arguments
          timeout = setTimeout(() => {
            fn(...args)
          }, wait);
        }
     }

实际工作中很少会手写这些工具代码,一般都会使用lodash或者underscore这些工具库。分析下underscore的防抖代码使用定时器结合时间戳和闭包实现的非常巧妙

underscore版防抖

	const _debounce = function (func, wait, immediate) {
        var timeout, args, context, timestamp, result;
        var later = function () {
          var last = new Date().valueOf() - timestamp;
          if (last < wait && last >= 0) {
            timeout = setTimeout(later, wait - last);
          } else {
            timeout = null;
            if (!immediate) {
              result = func.apply(context, args);
              if (!timeout) context = args = null;
            }
          }
        };
        return function () {
          context = this;
          args = arguments;
          timestamp = new Date().valueOf();
          var callNow = immediate && !timeout;
          if (!timeout) timeout = setTimeout(later, wait);
          if (callNow) {
            result = func.apply(context, args);
            context = args = null;
          }
          console.log(result, 22);
          return result;
        };
      };


// JS解释器遇到_debounce(func, 4000, false) 会将func做防抖处理生成一个新的函数设为d_func, 并将_debounce内定义的变量(timeout, args, context, timestamp, result, later)以闭包的形式留存,供d_func使用。
      // 事件触发时实际情况是:  window.addEventListener('resize', d_func })


      // d_func函数执行流程分析
      // 首次触发:(timeout为null)
        // context = this; 保存this指向到context,此处this指向事件的调用者节点boxNode(<div class="box"></div>)
        // args = arguments; 保存匿名函数func的入参到args
        // timestamp = new Date().valueOf(); 记录此次触发的时间戳
        // var callNow = immediate && !timeout; 创建标识 callNow  条件是 immediate为true, 并且不存在timeout(不存在即是首次触发)
            // immediate为false的话 只会执行定时器的任务
        // if (!timeout) timeout = setTimeout(later, wait); 创建定时器
        // callNow为真时执行 回调函数  只有immediate为真时并且首次触发时才会执行
          // result = func.apply(context, args); 绑定this,传入参数
          // context = args = null; 释放内存 

      // 时间间隔内再次触发:
        // 依旧是保存this
        // 保存arguments
        // 刷新触发的时间戳timestamp
        // 判断timeout是否存在 (此时timeout肯定存在)
            // 此时这个匿名函数func内只负责刷新 timestamp, 等待定时器执行
            // 定时器执行时 调用later函数
                // later函数执行
                    // 获取当前时间和触发时间timestamp的间隔last 可以叫做触发间隔
                    // last小于_debounce设置的间隔wait 重新设置timeout  setTimeout(later, wait - last); 并把间隔设置为剩余时间间隔
                    // last >= wait 执行  释放timeout=null immediate为false时,执行func.apply(context, args); 释放 context = args = null。

      // 以timeout变量为标识,分辨是首次触发还是,触发间隔wait内的重复触发。避免了常规写法的多次设置/清除定时器。

假设要处理window的resize事件防抖:

const func = (a) => {
   console.log("窗口尺寸变化了......");
};
window.addEventListener('resize', _debounce(func, 500))

如果想往func传参数a, 可以

const d_func = _debounce(func, 500)
window.addEventListener('resize', ()=>{d_func(‘我是传入的参数’)})
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值