闭包的使用场景,防抖为什么要使用闭包详解

  • 在面试中经常会被用到闭包的使用场景,百度的时候很多答案准备的例子都是防抖函数,
  • 有的面试官就会说,我不想听这个答案,还有其他例子吗?(毕竟网络是共享的,这个答案准备的比较多,面试官不想听很正常,可以结合自己的项目展开说说)
  • 如果你说暂时没想好其他例子,面试官还是会给面子,会继续往下问的
  • 那就会问:那就说说防抖为什么要用闭包吧,不用闭包行吗
    (这是真实的面试经历,所以为了下次的面试,我还是整理下学习笔记,给以后的自己看吧)
  • 首先要理解什么是防抖?

函数防抖(debounce),就是指触发事件后,在 n 秒内函数只能执行一次,如果触发事件后在 n 秒内又触发了事件,则会重新计算函数延执行时间。

知道了定义,现在就来一步一步的实现吧

// 首先写一个debounce函数吧,
function debounce(){
}
// 拆解概念: 
// 1,n 秒内只能执行一次,所以需要一个setTimeout(()=>{fn()},delay),
// 2, 如果触发事件后在 n 秒内又触发了事件,则会重新计算函数延执行时间。所以n秒内如果再次触发,就清除上一次的函数,那就需要用用一个变量timer 来保存上一个setTimeout返回值,好用clearTimeout来取消执行,存在就清除然后重新计时,大概框架就是  
 function debounce (fn,delay) {
    var timer
    if (timer) {
      clearTimeout(timer)
    }
    timer = setTimeout(() => {
      fn()
    }, delay)
  }
// 然后你会发现 这样写的话每次执行debounce ,timer都会被重新创建,然后执行完debounce后,里面的变量就被销毁了,这样就会创建出很多个延迟没执行的函数,一到时间就执行了,并不会重新计算时间
//接下来做个改造,把timer 提到全局作用域内,
 var timer 
  function debounce (fn, delay) {
    if (timer ) {
      clearTimeout(timer)
    }         
    timer  = setTimeout(() => {
        fn()      
      }, delay)    
  }

上面的函数虽然也能实现防抖的功能,但是如果有多个要防抖的事件,要定义多个变量,写多个函数吗?
第一个方法之所以要定义多个变量,因为他们共用同一个作用域(全局作用域),如果他们能有自己的作用域,相互之间不影响不就好了。

那就要定一个私有变量,并且能在外部访问,那闭包就是一个选择

 function debounce (fn, delay) {
    var timer
    function inside() {
      clearTimeout(timer)
      timer = setTimeout(() => {         
        fn.apply(this,arguments)
      }, delay)
    }
    return  inside
    /*
    如果这个函数 不用return  每次点击执行的就是debounce 函数,inside也被立即执行,
    debounce执行完之后,里面的变量和inside函数就被销毁了,所以每次点击都会执行debounce函数,
    timer都是新创建的,就没办法清除上一个timer
    */   
  }
 /* 
 如果这里return 一个函数,那就是inside在没执行的时候就被return 出去了;
 过程为:
 没有点击的时候debounce就会被先调用, debounce函数被调用的时候,timer初始化,并返回一个inside函数,
 但是inside还没有执行,debouce函数被调用后,里面的timer并没有被销毁,因为inside函数还在引用 ,当第一次点击的时候才会触发inside函数,
 
 inside函数执行的时候debounce作用域中timer被赋值,inside执行结束,并不能销毁debounce作用域中的timer, 
第二次点击的时候,inside再次执行使用的还是debounce作用域中timer,inside保持了debounce的作用域并有使用权限

当在n秒内再次触发点击可以判断上个定时器在不在,在的话就要先清除,重新赋值并重新计时,
 */

上面的例子因为是setTimeout 用的是 箭头函数 所以this和上层的this同一个指向,把this指向执行执行环境,要不然fn中的this指向全局变量

  • 如果setTimeout(function(){ fn() }, delay) 不使用箭头函数 请参考下面的写法(this指向问题这里就不做解释了)
 function debounce (fn, delay) {
    var timer
    function inside() {
      clearTimeout(timer)
      const that=this
      const args= arguments
      timer = setTimeout(function(){         
        fn.apply(that,args)
      }, delay)
    }
    return  inside
  }

下面是完整例子:

<!DOCTYPE html>
<html>

<head>
  <meta charset='utf-8' />
  <meta name="viewport"
    content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
  <title>test</title>
</head>

<body>
  <button id="test">点击</button>

</body>
<script>
  function debounce (fn, delay) {
    var timer
    return function () {
      clearTimeout(timer)
      timer = setTimeout(() => {
        fn.apply(this, arguments)
      }, delay)
    }
  }

  var button = document.getElementById('test')

  function handleClickAA () {
    console.log(this, 'aa')
    console.log('我点击了aa')
  }

  button.addEventListener('click', debounce(handleClickAA, 2000))


</script>


</html>

闭包的优点:模块化了代码, 没有污染全局变量,延长了形参的生命周期

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值