首页 > 其他分享 >练习6:节流和防抖实现

练习6:节流和防抖实现

时间:2022-08-19 15:57:05浏览次数:69  
标签:function 防抖 练习 节流 args timer now wait previous

节流

函数节流指的是某个函数在一定时间间隔内(例如 3 秒)只执行一次,在这 3 秒内 无视后来产生的函数调用请求,也不会延长时间间隔。3 秒间隔结束后第一次遇到新的函数调用会触发执行,然后在这新的 3 秒内依旧无视后来产生的函数调用请求,以此类推。

此时「管道中的水」就是我们频繁操作事件而不断涌入的回调任务,它需要接受「水龙头」安排;「水龙头」就是节流阀,控制水的流速,过滤无效的回调任务;「滴水」就是每隔一段时间执行一次函数,「3 秒」就是间隔时间,它是「水龙头」决定「滴水」的依据。

比如:提交按钮点击、下拉加载更多、onSrcoll、onresize等事件

实现

我们通常有两种方法实现节流

方案1:

function throttle(func, wait = 1000) {
  let previous = 0
  return function(...args) {
    const now = +new Date()
    if (now - previous > wait) {
      previous = now
      func.apply(this,args)
    }
  }
}
const throttleTask = throttle(() => console.log('节流函数运行了'),2000)
setInterval(throttleTask,500)

方案2:

function throttle2(func, wait = 1000) {
  let timer = null
  return function(...args) {
    if (!timer) {
      timer = setTimeout(() => {
        func.apply(this,args)
        timer = null
      },wait)
    }
  }
}
const throttleTask = throttle2(() => console.log('节流函数运行了'),2000)
setInterval(throttleTask,500)

方案1的特点是:立刻执行事件,并且事件停止后不会继续执行了
方案2的特点是:延迟了执行事件,并且事件停止后还可以执行一次事件

underscore 节流的实现

上述代码实现了一个简单的节流函数,不过 underscore 实现了更高级的功能,即新增了两个功能,

  1. 配置是否需要响应事件刚开始的那次回调( leading 参数,false 时忽略)
  2. 配置是否需要响应事件结束后的那次回调( trailing 参数,false 时忽略)

所以需要搭配上述方案1和方案2共同实现。

// underscore throttle 实现
function throttle_underscore(func, wait, options = {}) {
  let timer, context, previous = 0, args,result
  let later = function() {
    // 当设置 { leading: false } 时
    // 每次触发回调函数后设置 previous 为 0
    // 不然为当前时间
    previous = options.leading === false ? 0 : (+new Date())
    // 防止内存泄漏,置为 null 便于后面根据 !timeout 设置新的 timeout
    timer = null
    result = func.apply(context, args)
    if (!timeout) context = args = null
  }
  let throttled = function() {
    let now = +new Date()
    context = this
    args = arguments
    if (!previous && options.leading == false) previous = now
    let remaining = wait - (now - previous)
    if (remaining < 0 || remaining > wait) {
      if (timer) {
        clearTimeout(timer)
        timer = null
      }
      previous = now
      result = func.apply(context, args)
      if (!timer) {
        // 这应该只是为了垃圾回收
        context = args = null;
      }
    } else if (!timer && options.trailing) {
      timer = setTimeout(later, remaining)
    }
    return result
  }
  return throttled
}

防抖

防抖函数 debounce 指的是某个函数在某段时间内,无论触发了多少次回调,都只执行最后一次。假如我们设置了一个等待时间 3 秒的函数,在这 3 秒内如果遇到函数调用请求就重新计时 3 秒,直至新的 3 秒内没有函数调用请求,此时执行函数,不然就以此类推重新计时。

举一个小例子:假定在做公交车时,司机需等待最后一个人进入后再关门,每次新进一个人,司机就会把计时器清零并重新开始计时,重新等待 1 分钟再关门,如果后续 1 分钟内都没有乘客上车,司机会认为乘客都上来了,将关门发车。

此时「上车的乘客」就是我们频繁操作事件而不断涌入的回调任务;「1 分钟」就是计时器,它是司机决定「关门」的依据,如果有新的「乘客」上车,将清零并重新计时;「关门」就是最后需要执行的函数。

普通版实现如下:

function debounce(func, wait = 1000, immediate){
  let timer
  return function(...args) {
    if (timer) {
      clearTimeout(timer)
    }
    if (immediate && !timer) {
      // 第一次立即执行
      func.apply(this,args)
    }
    timer = setTimeout(() => {
      func.apply(this,args)
    }, wait)
  }
}
const betterFn = debounce(() => console.log('fn 防抖执行了'), 1000, true)
// 第一次触发 scroll 执行一次 fn,后续只有在停止滑动 1 秒后才执行函数 fn
document.addEventListener('scroll', betterFn)

加强版的节流:
wait 时间内重复触发会使用防抖只触发最后一次。如果在wait时间后,必须触发一次。
这样可以解决用户,一直在时间内不停触发防抖,导致fn始终无法执行的情况。

function throttleDebounce(fn, wait) {
  let previous = 0, timer = null
  return function(...args) {
    let now = +new Date()
    if (now - previous >= wait) {
      previous = now
      fn.apply(this, args)
    } else {
      if (timer) clearTimeout(timer)
      timer = setTimeout(() =>  {
        previous = now
        fn.apply(this, args)
      }, wait)
    }
  }
}
const betterFn2 = throttleDebounce(() => console.log('fn 节流执行了'), 1000)
// 第一次触发 scroll 执行一次 fn,每隔 1 秒后执行一次函数 fn,停止滑动 1 秒后再执行函数 fn
document.addEventListener('scroll', betterFn2)

标签:function,防抖,练习,节流,args,timer,now,wait,previous
From: https://www.cnblogs.com/xuweikang/p/16602248.html

相关文章

  • 练习5:常见Array原型方法实现
    Array.prototype.mapArray.prototype.map2=function(callbackfn,thisArg){if(this==null){thrownewTypeError('Cannotreadproperty"map"ofnullor......
  • 练习7:函数记忆相关
    何为函数记忆函数记忆是指将上次的计算结果缓存起来,当下次调用时,如果遇到相同的参数,就直接返回缓存中的数据。常用于,复杂且有重复的计算。例如:斐波那契数列的计算under......
  • 练习8:最大公约数和最小公倍数问题
    最大公约数的计算,用到辗转相除法例如:求gcd(24,10),可以转换为gcd(10,4),然后是gcd(4,2),然后是(2,0),最好得出结果是2方法1:functiongcd(a,b){vartempif......
  • 第四章 2 数据类型-字符串 练习题
    第四章2数据类型-字符串练习题基础知识1\python语句"".join(list('hellowordld!'))的执行结果是:helloworld!#join()函数,是字符串内置的一个函数,在classstr下面a......
  • 防抖和节流的介绍及实现
    防抖概述:在规定时间内只执行一次(执行最后一次)举个例子:电梯关门案例a进入电梯等待5s后就可以上升了在a等待了4s中后b过来那么之前的等待就结束了开始新的等待......
  • #10007. 「一本通 1.1 练习 3」线段
    #include<bits/stdc++.h>usingnamespacestd;structnode{ intl,r;};boolcmp(nodex,nodey){ returnx.r<y.r;}classSolution{ public: intsolve(vector......
  • MYSQL练习题(45题)
    建表语句--学生表StudentcreatetableStudent(SIdvarchar(10),Snamevarchar(10),Sagedatetime,Ssexvarchar(10));insertintoStudentvalues('01','赵雷',......
  • 闭包节流和防抖
    <style>div{width:200px;height:200px;background-color:skyblue;margin:50pxauto;}</style><div></di......
  • #10006. 「一本通 1.1 练习 2」数列分段
    #include<bits/stdc++.h>usingnamespacestd;classSolution{ public: intsolve(vector<int>num,intm) { intans=1,sum=0; for(inti=0;i<num.size(......
  • C++primer练习15.1-14
    练习15.1什么是虚成员?::需要派生类自己定义的成员练习15.2protected访问说明符与private有何区别?::protected允许派生类访问,private一律不允许访问练习15.3定义你自己的......