闭包的笨蛋教学

302 阅读3分钟

Day 1

一生之敌,闭包

  • LHS和RHS:

    • console.log( a );

      其中对 a 的引用是一个 RHS 引用,因为这里 a 并没有赋予任何值。相应地,需要查找并取得 a 的值,这样才能将值传递给 console.log(..)

    • a = 2;

      这里对 a 的引用则是 LHS 引用,因为实际上我们并不关心当前的值是什么,只是想要为 = 2 这个赋值操作找到一个目标。

  • 思考下下面这个例子:涉及了多少引用

    function foo(a) { //这里把2赋值给参数a,是LHS

    console.log( a ); // 2 //这里寻找a。是RHS.还有寻找console和log都是RHS

    }

    foo( 2 ); //这里对foo进行了RHS

  • 函数作用域:没一个函数都有属于自己的‘气泡‘,外部无法进入

  • IIFE自调用的匿名函数,它具有匿名函数的所有优势,值得推广

闭包

  • 闭包这个东西,当初刚接触时,听了太多了流言蜚语,导致对他有一种本能的恐惧,感觉很复杂,很不能理解,关于闭包的文章看过的数不胜数,今天用自己的想法来谈一谈闭包这个东西,希望能让大家清晰的认识闭包,

  • 首先问题是什么是闭包:

    • 通俗的说, 闭包是指有权访问另外一个函数作用域中的变量的函数。可以理解为(能够读取一个函数作用域的变量的函数)
    • 无论通过何种手段将内部函数传递到所在的词法作用域以外,它都会持有对原始定义作用域的引用,无论在何处执行这个函数都会使用闭包。
  • 其次为什么要使用闭包

    • 好处

      • 让外部访问函数内部变量成为可能;
      • 局部变量会常驻在内存中
      • 可以避免使用全局变量,防止全局变量污染
      • 可以模仿块级作用域
    • 坑处

      • 会造成内存泄漏(有一块内存空间被长期占用,而不被释放)
      • 闭包找到的是同一地址中父级函数中对应变量最终的值(在循环中的闭包函数)
      • this的指向问题
    • 应用场景

例子

  • 例子1

    function funA(){
      var a = 10;  // funA的活动对象之中;
      return function(){   //匿名函数的活动对象;
            alert(a);
      }
    }
    var b = funA();
    b();//10
    

    这个例子里,调用funA里的匿名函数,他打印出了funA作用域的变量

  • 例子2

    function outerFn(){
      var i = 0; 
      function innerFn(){
          i++;
          console.log(i);
      }
      return innerFn;
    }
    var inner = outerFn();  //每次外部函数执行的时候,都会开辟一块内存空间,外部函数的地址不同,都会重新创建一个新的地址
    inner();
    inner();
    inner();
    var inner2 = outerFn();
    inner2();
    inner2();
    inner2();   //1 2 3 1 2 3
    

    这里外部函数每次执行,都会开辟一个新的空间,内部函数作用外部函数的变量都是在他对应的那个空间中,所以inner和inner2他们的累加都是独立的

  • 例子3

    var i = 0;
    function outerFn(){
      function innnerFn(){
           i++;
           console.log(i);
      }
      return innnerFn;
    }
    var inner1 = outerFn();
    var inner2 = outerFn();
    inner1();
    inner2();
    inner1();
    inner2();//1234
    

    这里虽然开辟了两个独立的空间,但是i保存的是window对象上,所以他们操作的是同一个对象,所以值会累加,

  • 例子4

    function outerFn(){
    var i = 0;
      function innnerFn(){
          i++;
          console.log(i);
      }
      return innnerFn;
    }
    var inner1 = outerFn();
    var inner2 = outerFn();
    inner1();
    inner2();
    inner1();
    inner2();    //1 1 2 2
    

    这个例子说明,独立空间的操作,和运行的顺序无关

  • 例子5

    (function() { 
      var m = 0; 
      function getM() { return m; } 
      function seta(val) { m = val; } 
      window.g = getM; 
      window.f = seta; 
    })(); 
    f(100);
    console.info(g());   //100  闭包找到的是同一地址中父级函数中对应变量最终的值
    

    这个例子里,虽然按照顺序是getm先执行,他获得的是m=0,但是闭包里,找到的同一地址中父级函数对应变量的最终值,所以找到的是函数seta赋值后的m

本文使用 mdnice 排版