闭包详细解析及运用闭包实现防抖、节流函数

794 阅读4分钟

什么是闭包

概念:既重用变量又保护变量不被污染的一种编程方法。
运用:只要希望给一个函数,保存一个即可反复使用,又不会被外界污染的专属局部变量时,就用闭包。

如何使用闭包

  1. 外层函数包裹要保护的变量和使用变量内层函数
  2. 在外层函数内部返回内层函数对象。
  3. 调用外层函数用变量接住返回的内层函数对象。 举例:
//第1步: 用外层函数包裹要保护的变量和内层函数
function fun(){
    var total= 1000
    //第2步: 返回内层函数对象
    return function(money){
    // 来源于外部,进来之后可理解为自立门户了 。
    // 既和外部产生了羁绊,有独立于外部。
    total-=money; 
    console.log(`花了${money}还剩${total}元`)
    }
}
    //第3步: 调用外层函数,用变量接住内层函数对象
    var pay= fun()
    //pay接住的就是fun()返回出来的内层函数对象
    pay(100); // 900
    pay(100); // 800
    total = 0;// 这里的total=0没有影响后边的total计算说明,total被保护起来,只能pay函数使用。
    var payTwo= fun()
    payTwo(100); // 900
    payTwo(100); // 800

最内层函数,由内向外有三级作用域链:自己、外层函数的作用域、全局

解析:为什么形成闭包就可以保存变量不被释放

重点:所有函数调用完只清空作用域链中离自己近的一个作用域

释放的前提是:这个变量没有被引用,被垃圾回收器释放。
我们通俗的认为:程序执行完了之后,就被释放了。但在这边恰恰相反-由于return function(money){total -= moeny } 里面引用了total 所以不会被释放。
换句通俗易懂的话就是:因为fun函数的作用域对象被内层函数的作用域链引用着,无法释放,就侥幸存活下来!而且,只有内层函数pay知道total变量的存储位置——专属私密

强调:因为内层函数只是定义,未加()调用。所以,内层函数中的代码不执行!

解析:()的作用:立即执行,通俗的理解来说是调用。你定义了一段代码片段,不去调用就没有()想调用有();

总结:到底什么是闭包

  • 闭包就是每次调用外层函数时,临时创建的函数作用域对象。
  • 闭包也是一个对象。
  • 为什么外层函数作用域对象能留下来?
    • 因为被内层函数对象的作用域链引用着,无法释放。

image.png

闭包的缺点

由于闭包藏得很深几乎找不到所以,极容易造成内存泄漏!

解决:及时释放不用的闭包。

如何释放不用的闭包对象?

保存内层函数对象的变量赋值为null;导致函数名变量内层函数对象分开

    pay= null

小练几题,理解闭包

题一:nadd函数立即执行
    function fun1(){
        var total= 1000;
        // 最后添加的小括号,意思是:函数立即执行了。
        nadd= function(){total++ ; console.log("-----",total)}() //1001
        return function(money){
          total-=money;
          console.log(total)
        }
      }
      var pay= fun1();
      pay(100) // 901
      pay(100) // 801
题二:调用nadd函数
    function fun1(){
        var total= 1000;
        nadd= function(){total++ ; console.log("-----",total)}
        return function(money){
          total-=money;
          console.log(total)
        }
      }
      var pay= fun1();
      pay(100) // 900
      nadd()   // 901
      pay(100) // 801
题三:循环创建
     function fun(){//妈妈
          arr=[];//不算红包,因为内层函数中没有用到!
          //但是arr是给未声明的变量强行赋值
          //自动在全局创建该变量
          for(var i=0/*红包*/;i<3;i++){//循环三次
              //强行给外部全局变量中添加新函数——破腹产
              // new Function()
              arr[i]=function(){//反复创建了3个孩子
                  console.log(i);//因为只是创建函数,所以这里暂时不执行。
              } 
          }
          // for结束后,i=3
      }
          fun();
          //妈妈一次刨妇产生了3个孩子,包了一个共用的红包,红包里是3块钱
          //三个兄弟,输出的结果是一样的,都是3
          arr[0]();//3
          arr[1]();//3
          arr[2]();//3

使用闭包实现防抖节流函数

节流函数

概念:一个函数执行一次后,只有大于设定的执行周期后才会执行第二次(有个需要频繁处罚函数,储于优化性能角度,在规定时间内,只让函数触发的第一次生效,后面不生效)

    body,html{
        height:200%;
    }
    functinon throttle(fn,time){
        var lastTime = 0;
        // 通过闭包来保存 lastTime 的状态
        return function(){
            var nowTime = Date.now();
            if(nowTime - lastTime > time){
                fn();
                //同步时间
                lastTime = nowTime;
            }
        }
    }
    document.onscroll= throttle(function(){
        console.log('你触发了滚动事件')
    },2000)

节流函数直观感受:

image.png

防抖函数

概念:一个需要频繁触发的函数,在规定时间内,只让最后一次生效,前面的不生效;

    <button id="btn"></button>
    function debounce(fn,delay){
        var timer;
        return function(){
            clearTimeout(timer);
            timer = setTimeout(()=>{
                fn();
            },delay)
        }
    }
    
    document.getElementById('btn').onclick = debounce(function(){
	console.log("点击事件被触发了 !")
	},1000);

image.png

总结区别:

假如:对防抖函数和节流函数同时不间断操作10秒,函数的时间间隔都为:2秒
节流函数:会被触发五次;
防抖函数:只会被触发一次

出自东神(* ̄︶ ̄)!