# 闭包

作者: 多叉树;转载须注明出处

在介绍闭包之前,我们先看看是什么全局变量和局部变量

# 全局变量和局部变量

局部变量:定义在函数内部的变量(只能在内部被访问) 形参也是一种局部变量

全局变量:不在函数内部定义的变量, 就称为全局变量,全局变量在任何函数内都可以被访问和修改

假如我们在函数内部 定义了一个和外部相同名字的变量, 那么在函数内部是用的哪个变量呢?

<script>
    var ccc = 5;
    function inner() {
        var ccc = 4;
        console.log(ccc);
    }

    inner();

  </script>

以上结果会输出4 , 因为在执行过程中, 会由内向外寻找变量的定义,所以局部变量在函数内部比全局变量更有优先被使用的权力

再看一个例子, 假如我们打印外部的ccc变量会输出什么?

<script>
    var ccc = 5;
    function inner() {
        var ccc = 4;
    }

    inner();

    console.log(ccc);

</script>

上面的例子会输出 5, 因为函数内部定义的变量无法被外部看到

我们再来看一个比较奇葩的例子

<script>
    var ccc = 5;
    function inner() {
        ccc += 1;
        var ccc = 4;
        console.log(ccc);
    }

    inner();

</script>

这个例子第一次打印会输出 NaN , 第二次打印会输出 4

第二次打印好理解, 因为我们已经知道局部变量优先被使用。

第一次比较奇怪, 按道理讲, 在函数内部也应该能引用到外部的全局变量ccc,但是程序的运行结果和我们的推断相反,这是因为js语言有个比较坑的知识点;即在函数执行前,语法规定把变量的定义提升到 函数的前面。

上面的例子在运行的时候浏览器是这么理解的

<script>
    var ccc = 5;
    function inner() {
				var ccc = undefined;
        ccc += 1;
        ccc = 4;
        console.log(ccc);
    }

    inner();

</script>

这种特性就叫做函数变量提升, 把一个后面定义变量, 提升到前面,因为前面有这个地方的引用。

# 局部函数

一个函数内部也可以定义另一个函数内部

<script>
        function outer() {

            function inner() {
                console.log("innser函数")
            }

            inner()

        }

        outer();
  </script>

上面的inner() 函数只能再 outer() 函数内部调用

# 特别注意

如果不加修饰词定义一个变量的话, 此变量将被视为全局变量,即使它定义在了函数内部

<script>

        function fa() {
            quanju = 10;
        }
        fa();
        console.log(quanju);

    </script>

# 闭包

# 概念

从概念上讲,闭包就是一个函数以及捆绑在此函数周围环境状态的引用组合, 每创建一个函数都会产生一个闭包

这个定义比较抽象,下面,我们举个栗子

<script>
      function outter(){
					
					var cons = '常量小常'
          
          return function() {
              return cons;
          } 
      }
			
			// 得到外部函数的内部函数
      var consFun= outter(1,2);
      // 执行内部函数
      consFun();
</script>

这个例子最终会输出 ‘常量小常’, 虽然内部函数是在外部执行的, 但它依然能够找到定义时候的一些常量值。这个就是一种闭包的表现形式

Untitled

换句话说,就是函数在执行的时候,能够还原它定义时所处的环境,能够按照定义时候的样子去执行

# 模拟私有变量

很多语言都支持私有变量, 就是将一个变量或者方法声明为私有,只能被内部使用,外部要想使用只能通过定义的一些公共方法访问

Js原生不支持这种语法,但可以通过闭包模拟这种私有变量的特性

下面就是一个闭包模拟私有变量的例子 , privateCounter  变量不能直接被外部访问,只能通过特定的方法操作。这个例子来源于此:

用闭包模拟私有变量

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures#%E7%94%A8%E9%97%AD%E5%8C%85%E6%A8%A1%E6%8B%9F%E7%A7%81%E6%9C%89%E6%96%B9%E6%B3%95

<script>
      var Counter = (function () {
      var privateCounter = 0;
      function changeBy(val) {
          privateCounter += val;
      }
      return {
          increment: function () {
          changeBy(1);
          },
          decrement: function () {
          changeBy(-1);
          },
          value: function () {
          return privateCounter;
          },
      };
      })();

      console.log(Counter.value()); /* logs 0 */
      Counter.increment();
      Counter.increment();
      console.log(Counter.value()); /* logs 2 */
      Counter.decrement();
      console.log(Counter.value()); /* logs 1 */

  </script>