minjs1cn / weekly-learning

每周学习分享打卡
0 stars 0 forks source link

11-【经典面试】闭包,防抖和节流 #11

Open Brand0n-Zhang opened 3 years ago

Brand0n-Zhang commented 3 years ago
Brand0n-Zhang commented 3 years ago

https://github.com/Amarok1217/rumination/blob/master/JavaScript%E7%9B%B8%E5%85%B3/%E8%AF%AD%E8%A8%80%E7%89%B9%E6%80%A7/%E9%97%AD%E5%8C%85/%E9%97%AD%E5%8C%85.md

https://github.com/Amarok1217/rumination/blob/master/JavaScript%E7%9B%B8%E5%85%B3/%E8%BF%90%E8%A1%8C%E6%97%B6/%E9%98%B2%E6%8A%96%E5%92%8C%E8%8A%82%E6%B5%81/%E9%98%B2%E6%8A%96%E8%8A%82%E6%B5%81.md

asdzxc01 commented 3 years ago

https://zhuanlan.zhihu.com/p/38313717

OceanApart commented 3 years ago

闭包

一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。

除了一些特殊任务(模拟私有方法),应该避免在函数中创建函数,比如构造函数中创建的函数在每次调用时,都会重新赋值,存在处理速度和内存消耗的问题。

防抖

// 防抖,在触发后 n 秒后再执行,如果重新触发,重新计时,
// 能控制是否立即执行,而不是等待后执行
// 保留函数的 this 指向
// 能够取消

function debounce (func, wait, immediate) {
  var timeout, result

  // 节流过的函数
  var debounced = function () {
    var context = this
    var args = arguments

    if (timeout) clearTimeout(timeout)

    // 立即执行模式
    if (immediate) {
      var callNow = !timeout

      if (callNow) {
        // 如果倒计时结束,立即执行函数
        result = func.apply(context, args)
      }

      // 倒计时重新开始
      timeout = setTimeout(function () {
        // 倒计时结束
        timeout = null
      }, wait)
    } else {
      // 超时后执行
      timeout = setTimeout(function () {
        func.apply(context, args)
      }, wait)
    }

    // 立即执行时返回函数执行结果
    return result
  }

  // 提供取消函数
  debounced.cancel = function () {
    clearTimeout(timeout)
    timeout = null
  }

  return debounced
}

节流

// 节流持续触发事件,每隔一段时间,只执行一次事件。
// options.leading  事件触发立即执行
// options.trailing 停止触发事件后,超时时仍会执行一次
function throttle (func, wait, options) {
  var context, args, timeout, previous

  if (!options) options = {}

  var throttled = function () {
    var now = new Date().getTime()

    context = this
    args = arguments

    // 首次触发不需要立即执行时
    if (!previous && options.leading === false) previous = now

    // 距离下次执行还剩的时间
    var remaining = wait - (now - previous)

    // 超时 或 系统时间调整过导致剩余时间大于等待时间
    if (remaining <= 0 || remaining > wait) {
      if (timeout) {
        // 如果时间已过,但计时器仍未执行,需要手动清除
        clearTimeout(timeout)
        timeout = null
      }

      previous = now

      func.apply(context, args)

      if (!timeout) {
        // 如果不存在超时计数器,可以清空变量以便回收内存
        context = args = null
      }
    } else if (!timeout && options.trailing !== false) {
      // 超时执行未设置 && 打开最后一次触发后超时执行
      timeout = setTimeout(function () {
        if (options.leading !== false) {
          previous = new Date().getTime()
        }

        func.apply(context, args)

        clearTimeout(timeout)
        timeout = null

        if (!timeout) {
          // 如果不存在超时计数器,可以清空变量回收内存
          context = args = null
        }
      }, remaining)
    }
  }

  // 取消函数
  throttled.cancel = function () {
    clearTimeout(timeout)
    previous = timeout = null
  }

  return throttled
}
wucuiping commented 3 years ago

https://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html