防抖/节流的优缺点分析和改良升级

一. 防抖和节流的作用:
    防止高频的事件触发造成不必要的计算消耗, 比如, 典型的应用场景:

  •     搜索框 input事件;
  •     鼠标移动 mousemove事件;
  •     视窗大小变化 resize事件;

二. 防抖的优缺点:
    创建timer, 当新的触发出现时, 销毁老的timer.
    性能消耗稍大于节流(不过, 这点性能差别算不了什么).
    如果连续的调用间隔一直小于delay, 会造成长时间不触发.

三. 节流的优缺点:
    通过时间差来判断要不要放弃当次触发,
    不需要创建和销毁timer. 
    但是因为根据前一次的时间差来判断放弃, 可能会扔掉最后一次.
     如果最后一次包含重要的状态切换, 则可能丢失.

四. 防抖+节流的升级版
    在防抖的基础上增加一个值, 用于记住上次的触发时间, 
    采用上次触发时间到当前时间的差, 用于计算真实的delay大小.
    当delay小到等于0的时候, clearTimeout就没有机会清掉前一次触发了.

五. 代码实现和测验:

function debounce(cb, delay) {                    // 防抖(回调, 延迟)
  let timer = null;                               // timer对象
  return function(...args) {                      // 返回闭包给外部调用, 收集外部调用的参数
    if(timer !== null) clearTimeout(timer);       // 先清除前一次timer
    timer = setTimeout(() => {                    // 设置delay timer
      cb && cb.apply(this, args);                 // 将外部参数传给回调
    }, delay);
  }
}

function throttle(cb, delay){                     // 节流(回调, 延迟)
  let lastTick = 0;                               // 上次发送时间点
  return function(...args) {                      // 返回闭包函数, 取到外部参数
    if (Date.now() - lastTick > delay) {          // 时间差大于delay, 才调用fn
        lastTick = Date.now();                    // 更新时间
        cb && cb.apply(this, args);               // 将外部参数传给回调
    }
  }
}

function debounce_throttle(cb, delay) {               // 防抖+节流(回调, 延迟)
  let timer = null;                                   // timer对象
  let lastTick = Date.now();                          // 上次发送时间
  return function(...args) {                          // 返回闭包给外部调用, 收集外部调用的参数
    if(timer !== null) clearTimeout(timer);           // 先清除前一次timer
    timer = setTimeout(() => {                        // 设置delay timer
      lastTick = Date.now();                          // 更新时间
      cb && cb.apply(this, args);                     // 将外部参数传给回调
    }, Math.max(0, delay - (Date.now() - lastTick))); // delay减去已消耗时间
  }
}

function A(a) {
  this.a = a;
  this.proc = function(...args) {
    console.log(`[${Date.now()/1000}] A.proc, this:${this.a}, args:${JSON.stringify(args)}`);
  }
}
let aa = new A(123);
let bb = new A(456);
let cc = new A(789);

let db = debounce(aa.proc.bind(aa), 1000);
let tt = throttle(bb.proc.bind(bb), 1000);
let dt = debounce_throttle(cc.proc.bind(cc), 1000);

let startTick = Date.now();
let trigger = setInterval(()=>{
  console.log(`[${Date.now()/1000}] interval call`);
  db(1,2,3);
  tt(4,5,6);
  dt(7,8,9);
  if (Date.now() - startTick > 4000) clearInterval(trigger);
}, 200);

六. 测验输出结果:

> node .\test.js
[1616485874.114] interval call
[1616485874.121] A.proc, this:456, args:[4,5,6]
[1616485874.315] interval call
[1616485874.516] interval call
[1616485874.716] interval call
[1616485874.914] A.proc, this:789, args:[7,8,9]
[1616485874.917] interval call
[1616485875.117] interval call
[1616485875.317] interval call
[1616485875.318] A.proc, this:456, args:[4,5,6]
[1616485875.518] interval call
[1616485875.717] interval call
[1616485875.914] A.proc, this:789, args:[7,8,9]
[1616485875.919] interval call
[1616485876.118] interval call
[1616485876.319] interval call
[1616485876.319] A.proc, this:456, args:[4,5,6]
[1616485876.519] interval call
[1616485876.72] interval call
[1616485876.914] A.proc, this:789, args:[7,8,9]
[1616485876.92] interval call
[1616485877.121] interval call
[1616485877.32] interval call
[1616485877.321] A.proc, this:456, args:[4,5,6]
[1616485877.521] interval call
[1616485877.722] interval call
[1616485877.915] A.proc, this:789, args:[7,8,9]
[1616485877.923] interval call
[1616485878.915] A.proc, this:789, args:[7,8,9]
[1616485878.937] A.proc, this:123, args:[1,2,3]
>

从结果看, 防抖因为连续触发的间隔小于设定的delay, 所以直到最后才获得触发机会.
节流前期正常触发, 遗落了最后一次触发.
只有第三个版本, 即能均匀触发, 又不丢失最后一次触发. 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值