每日前端进阶第五题:防抖(debounce)函数的作用是什么?有哪些应用场景,请实现一个防抖函数。

栏目: JavaScript · 发布时间: 6年前

内容简介:防抖是什么?防抖就是防止抖动,例如小朋友喜欢多动,停不下来,然后做家长的打一顿,让小朋友安静下来,这种行为就叫做防抖。(~o ̄3 ̄)~抖个机灵,哈哈哈~

防抖是什么?防抖就是防止抖动,例如小朋友喜欢多动,停不下来,然后做家长的打一顿,让小朋友安静下来,这种行为就叫做防抖。

(~o ̄3 ̄)~

每日前端进阶第五题:防抖(debounce)函数的作用是什么?有哪些应用场景,请实现一个防抖函数。

抖个机灵,哈哈哈~

抖动是一个很常见的控制函数在一定时间内执行多少次的技巧,其实就是确定了函数执行的最小间隔,如果还在这个间隔内触发函数,则重新计算。

举个栗子~

你家有七个儿子,大儿子叫大娃,二儿子叫二娃,三儿子叫三娃,四儿子叫四娃,五儿子叫五娃,六儿子叫六娃,七儿子叫七娃。由于七个孩子还小,他们的衣服你只能用手洗,一洗洗七套,一次要一个小时。如果洗到一半,你的七个孩子给你过来捣乱把衣服弄脏,那么你又得花一个小时去洗,不捣乱,那么你一个小时之后你的孩子衣服就又脏了,又可以再花一个小时来洗衣服了。

具体实现

鱼头注:以下代码来自优秀的JS库 lodash

// 判断是不是个对象
function isObject(value) {
  const type = typeof value
  return value != null && (type == 'object' || type == 'function')
}

/*
 * 创建一个 debounced 函数,延迟调用 func 直到 wait 之后
 * cancel 方法用于取消 debounced
 * flush 方法用于立即调用
 */

function debounce(func, wait, options) {
  let lastArgs,
    lastThis,
    maxWait,
    result,
    timerId,
    lastCallTime

  let lastInvokeTime = 0
  let leading = false
  let maxing = false
  let trailing = true

  // 绕过 requestAnimationFrame ,显式地设置 wait = 0
  const useRAF = (!wait && wait !== 0 && typeof root.requestAnimationFrame === 'function')

  if (typeof func !== 'function') {
    throw new TypeError('Expected a function')
  }
  wait = +wait || 0
  if (isObject(options)) {
    leading = !!options.leading
    maxing = 'maxWait' in options
    maxWait = maxing ? Math.max(+options.maxWait || 0, wait) : maxWait
    trailing = 'trailing' in options ? !!options.trailing : trailing
  }

  function invokeFunc(time) {
    const args = lastArgs
    const thisArg = lastThis

    lastArgs = lastThis = undefined
    lastInvokeTime = time
    result = func.apply(thisArg, args)
    return result
  }

  function startTimer(pendingFunc, wait) {
    if (useRAF) {
      root.cancelAnimationFrame(timerId);
      return root.requestAnimationFrame(pendingFunc)
    }
    return setTimeout(pendingFunc, wait)
  }

  function cancelTimer(id) {
    if (useRAF) {
      return root.cancelAnimationFrame(id)
    }
    clearTimeout(id)
  }

  function leadingEdge(time) {
    // 重置任何 最大等待时间的计时器
    lastInvokeTime = time
    // 启动计时器
    timerId = startTimer(timerExpired, wait)
    // 返回调用结果
    return leading ? invokeFunc(time) : result
  }

  function remainingWait(time) {
    const timeSinceLastCall = time - lastCallTime
    const timeSinceLastInvoke = time - lastInvokeTime
    const timeWaiting = wait - timeSinceLastCall

    return maxing
      ? Math.min(timeWaiting, maxWait - timeSinceLastInvoke)
      : timeWaiting
  }

  function shouldInvoke(time) {
    const timeSinceLastCall = time - lastCallTime
    const timeSinceLastInvoke = time - lastInvokeTime

    // 判断是否应该调用函数
    return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
      (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait))
  }

  function timerExpired() {
    const time = Date.now()
    if (shouldInvoke(time)) {
      return trailingEdge(time)
    }
    // 重置定时器
    timerId = startTimer(timerExpired, remainingWait(time))
  }

  function trailingEdge(time) {
    timerId = undefined

    // 如果我有最后一个参数,就意味着函数需要调用
    // 至少执行一次防抖
    if (trailing && lastArgs) {
      return invokeFunc(time)
    }
    lastArgs = lastThis = undefined
    return result
  }

  function cancel() {
    if (timerId !== undefined) {
      cancelTimer(timerId)
    }
    lastInvokeTime = 0
    lastArgs = lastCallTime = lastThis = timerId = undefined
  }

  function flush() {
    return timerId === undefined ? result : trailingEdge(Date.now())
  }

  function pending() {
    return timerId !== undefined
  }

  function debounced(...args) {
    const time = Date.now()
    const isInvoking = shouldInvoke(time)

    lastArgs = args
    lastThis = this
    lastCallTime = time

    if (isInvoking) {
      if (timerId === undefined) {
        return leadingEdge(lastCallTime)
      }
      if (maxing) {
        // 处理循环调用
        timerId = startTimer(timerExpired, wait)
        return invokeFunc(lastCallTime)
      }
    }
    if (timerId === undefined) {
      timerId = startTimer(timerExpired, wait)
    }
    return result
  }
  debounced.cancel = cancel
  debounced.flush = flush
  debounced.pending = pending
  return debounced
}
复制代码

使用方法如下:

var 打印一些东西啦 = function (event) {
	console.log(event);
};

document.querySelector('html').addEventListener('click', debounce(打印一些东西啦, 1000));
复制代码
如果你、喜欢探讨技术,或者对本文有任何的意见或建议,你可以扫描下方二维码,关注微信公众号“

”,随时与鱼头互动。欢迎!衷心希望可以遇见你。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Hacking Growth

Hacking Growth

Sean Ellis、Morgan Brown / Crown Business / 2017-4-25 / USD 29.00

The definitive playbook by the pioneers of Growth Hacking, one of the hottest business methodologies in Silicon Valley and beyond. It seems hard to believe today, but there was a time when Airbnb w......一起来看看 《Hacking Growth》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

MD5 加密
MD5 加密

MD5 加密工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具