• 函数去抖(debounce)与 函数节流(throttle)


    以下场景往往由于事件频繁被触发,因而频繁执行DOM操作、资源加载等重行为,导致UI停顿甚至浏览器崩溃。

      1. window对象的resize、scroll事件

      2. 拖拽时的mousemove事件

      3. 射击游戏中的mousedown、keydown事件

      4. 文字输入、自动完成的keyup/keypress事件

      实际上对于window的resize事件,实际需求大多为停止改变大小n毫秒后执行后续处理;而其他事件大多的需求是以一定的频率执行后续处理。针对这两种需求就出现了debounce和throttle两种解决办法。

    一、什么是debounce

    1、定义:

    如果用手指一直按住一个弹簧,它将不会弹起直到你松手为止。

    也就是说当调用动作n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间。

    接口定义:

    /**
    * 空闲控制 返回函数连续调用时,空闲时间必须大于或等于 idle,fn 才会执行
    * @param fn {function}  请求关联函数,实际应用需要调用的函数
    * @param delay   {number}    空闲时间,单位毫秒
    * @return {function}    返回客户调用函数
    */
    debounce(fn,delay)

    2、简单实现:

    /**
    *
    * @param fn {Function}   实际要执行的函数
    * @param delay {Number}  延迟时间,也就是阈值,单位是毫秒(ms)
    *
    * @return {Function}     返回一个“去弹跳”了的函数
    */
    
    function debounce(fn, delay) {
    
      // 定时器,用来 setTimeout
      var timer
    
      // 返回一个函数,这个函数会在一个时间区间结束后的 delay 毫秒时执行 fn 函数
      return function () {
    
        // 保存函数调用时的上下文和参数,传递给 fn
        var context = this
        var args = arguments
    
        // 每次这个返回的函数被调用,就清除定时器,以保证不执行 fn
        clearTimeout(timer)
    
        // 当返回的函数被最后一次调用后(也就是用户停止了某个连续的操作),
        // 再过 delay 毫秒就执行 fn
        timer = setTimeout(function () {
          fn.apply(context, args)
        }, delay)
      }
    }

    其实思路很简单,debounce 返回了一个闭包,这个闭包依然会被连续频繁地调用,但是在闭包内部,却限制了原始函数 fn 的执行,强制 fn 只在连续操作停止后只执行一次。

    debounce 的使用方式如下:

    $(document).on('mouvemove', debounce(function(e) {
        // 代码
    }, 250))
    $('input').on('keyup', debounce(function(e) {
        // 发送 ajax 请求
    }, 300))

    二、什么是throttle

    1、定义

           如果将水龙头拧紧直到水是以水滴的形式流出,那你会发现每隔一段时间,就会有一滴水流出。

      也就是说预先规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某个事件被触发多次,只有一次能生效。

     接口定义:

    /**
    * 频率控制 返回函数连续调用时,action 执行频率限定为 次 / delay
    * @param delay  {number}    延迟时间,单位毫秒
    * @param action {function}  请求关联函数,实际应用需要调用的函数
    * @return {function}    返回客户调用函数
    */
    throttle(delay,action)

    2、简单实现

    /**
    *
    * @param fn {Function}   实际要执行的函数
    * @param delay {Number}  执行间隔,单位是毫秒(ms)
    *
    * @return {Function}     返回一个“节流”函数
    */
    function throttle(fn, threshhold) {
    
      // 记录上次执行的时间
      var last
    
      // 定时器
      var timer
    
      // 默认间隔为 250ms
      threshhold || (threshhold = 250)
    
      // 返回的函数,每过 threshhold 毫秒就执行一次 fn 函数
      return function () {
    
        // 保存函数调用时的上下文和参数,传递给 fn
        var context = this
        var args = arguments
    
        var now = +new Date()
    
        // 如果距离上次执行 fn 函数的时间小于 threshhold,那么就放弃
        // 执行 fn,并重新计时
        if (last && now < last + threshhold) {
          clearTimeout(timer)
    
          // 保证在当前时间区间结束后,再执行一次 fn
          timer = setTimeout(function () {
            last = now
            fn.apply(context, args)
          }, threshhold)
        // 在时间区间的最开始和到达指定间隔的时候执行一次 fn
        } else {
          last = now
          fn.apply(context, args)
        }
      }
    }

    原理也不复杂,相比 debounce,无非是多了一个时间间隔的判断,其他的逻辑基本一致。throttle 的使用方式如下:

    $(document).on('mouvemove', throttle(function(e) {
        // 代码
    }, 250))

    throttle 常用的场景是限制 resize 和 scroll 的触发频率

    总结:

    debounce 强制函数在某段时间内只执行一次,throttle 强制函数以固定的速率执行。在处理一些高频率触发的 DOM 事件的时候,它们都能极大提高用户体验。

  • 相关阅读:
    公司技术部与其他部门之间的那些事儿
    5万元百元大钞的"渣渣钱"重新拼接的软件方面的解决办法的思路。
    公司技术部门内部的发展变化过程。
    手机开发与360的那点事儿
    通用快排
    被中断的函数
    setjmp与longjmp
    setjmp在非主函数中调用
    array and structure
    check your input
  • 原文地址:https://www.cnblogs.com/xuepei/p/8351224.html
Copyright © 2020-2023  润新知