简单理解JavaScript闭包

闭包(closure)

MDN中的解释

函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起构成闭包(closure)。也就是说,闭包可以让你从内部函数访问外部函数作用域。在 JavaScript 中,每当函数被创建,就会在函数生成时生成闭包。
从技术角度讲,每个JavaScript函数都是闭包,因为他们都是对象且都关联到了作用域链。

举个例子

function f1(){
  var n=999;
  function f2(){
      alert(n); // 999
  }
}

其中f2函数作为f1的内部函数,f2函数可以读取f1函数的局部变量,则称f2为一个闭包

理解

  • 变量作用域

    JavaScript变量作用域有两种,一种是全局变量,一种是局部变量。
    JavaScript语言在函数内部就可以读取到全局变量。

      var a = 'hello';
      function f1(){
          console.log(a);
      }
      f1(); // hello
    

    而在函数外部就不能读取函数内部的局部变量。

      function f1(){
      	var a = 'hello';
      }
      console.log(a); // undefined
    
  • 链式作用域-如何从函数外部读取函数内部变量

    正常情况下,函数外部无法读取到函数内部的变量,而定义在函数内部的函数,可以读取到函数的局部变量,看之前的例子:

      function f1(){
        var n=999;
        function f2(){
            alert(n); // 999
        }
      }
    

    这是JavaScript特有的链式作用域的结构,即子对象会一级一级的向上寻找所有父对象的变量,所以父对象的对象对子对象都是可见的,反之不成立。

    再看一个例子:

      var n = 1000;
      function f1(){
        var n = 999;
        function f2(){
          alert(n);
        }
        return f2;
      }
      f1()(); // 输出结果是什么?
    

    上面这个例子中,我们将内部函数f2作为返回值,在外部进行调用执行,想一下函数f1在执行的时候会创建作用域链,而函数f2是定义在f1的作用域链中的,所以函数f2是被绑定在f1的作用域链上的,无论函数f2在哪里执行这种绑定依旧存在。所以在执行函数f2是,会先在f1的作用域链中查找变量,所以上面例子输出结果应为999。

  • 闭包概念

    上例中f2函数即为一个闭包,可以简单理解闭包就是一个可以读取函数内部变量的函数。由于JavaScript中只有函数内部的子函数可以读取函数的局部变量,也可以理解是定义在函数内部的函数。

闭包的用途

  • 读取函数内部的变量

  • 让变量值不被回收,一直在内存中

      function fn(){
      	var n = 1000;
        console.log(n++);
      }
      fn(); // 1000
      fn(); // 1000
    

    n作为内部变量,在第一次调用fn之后,即被清除掉不在内存中维护,再次调用fn又是初始化新的变量n,所以导致n的值没有发生变化

      function fn(){
      	var n = 1000;
        function f1(){
          console.log(n++);
        }
        return f1;
      }
      var f1 = fn();
      f1(); // 1000
      f1(); // 1001
    

    f1函数作为闭包,调用了fn的局部变量n,而f1被赋值给了全局变量,导致f1始终在内存中,而f1依赖于fn,所以fn也会始终在内存中,不会因为调用了f1之后就会被回收掉。

注意事项

  • 由于使用闭包不会回收掉父函数对象,增大内存消耗,可能会导致内存泄露或性能问题,需要在退出函数之前删除不适用的局部变量。
  • 闭包可以在函数外部修改父函数内部的局部变量的值,在使用过程中尽量避免。

应用场景

  • 封装变量

      function counter(){
          var privateCounter = 0; //私有变量
          function change(val){
              privateCounter += val;
          }
          return {
              increment:function(){   //三个闭包共享一个词法环境
                  change(1);
              },
              decrement:function(){
                  change(-1);
              },
              value:function(){
                  return privateCounter;
              }
          };
      };
      var counterA = counter();
      var counterB = counter();
      console.log(counterA.value());// 0
      counterA.increment();
      console.log(counterB.value());// 0 互不影响
      counterB.increment();
      console.log(counterA.value());// 1
      console.log(counterB.value());// 1
    

你可能感兴趣的:(javascript,javascript)