欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JS之闭包

程序员文章站 2022-04-24 22:42:15
闭包在红宝书中的解释就是:有权访问另一个函数作用域中的变量的函数。 1.变量作用域 全局变量:所有的函数外部定义的变量,它的作用域是整个script。 局部变量:定义在函数体内部的变量,作用域仅限于函数体内部。离开函数体就会无效。再调用就是出错。 举例如下-局部变量: a变量定义在fun函数内,是局 ......

闭包在红宝书中的解释就是:有权访问另一个函数作用域中的变量的函数。

1.变量作用域

全局变量:所有的函数外部定义的变量,它的作用域是整个script。

局部变量:定义在函数体内部的变量,作用域仅限于函数体内部。离开函数体就会无效。再调用就是出错。

举例如下-局部变量:

<script type="text/javascript">
    function fun(){
        var a = 100;
    }
    console.log(a);
</script>

a变量定义在fun函数内,是局部变量,所以它不能在外部被访问。

JS之闭包

举例如下-全局变量:

<script type="text/javascript">
    var c = 100;
    function fun(){
        var a = 100;
        console.log(c)
    }
    fun();
    console.log(c);
</script>

在全局定义一个全局变量c,不仅能在fun函数内部被访问,在函数外依旧能被访问。

JS之闭包

2.间接访问局部变量

<script type="text/javascript">
    function fun(){
        var a = 100;
        function fun1(){
            console.log(a);
        }
        fun1();
    }
    fun();
</script>

通过调用fun1把a打印出来,fun1是可以访问fun的所有变量

JS之闭包

3.作用域链

可以参考我的这篇文章js之预编译和执行顺序(全局和函数)可以更好的理解预编译的原理,为作用域链做准备。

举例:

<script type="text/javascript">
    var a = 100;
    function fun(){
        var b = 200
        function fun2(){
            var c = 300
        }
        function fun3(){
            var d = 400
        }
        fun2()
        fun3()
    }
    fun()
</script>

首先预编译,一开始生成一个go{

  a:underfined

  fun:function fun(){//fun的函数体

      var b = 200
      function fun2(){
        var c = 300
      }
      function fun3(){
      var d = 400
      }
      fun2()
      fun3()
    }

}

逐行执行代码,go{

  a:100

  fun:function fun(){//fun的函数体

      var b = 200
      function fun2(){
        var c = 300
      }
      function fun3(){
      var d = 400
      }
      fun2()
      fun3()
    }

}

当fun函数执行时,首先预编译会产生一个ao{

  b:underfined

  fun2:function fun2(){
       var c = 300
     }

  fun3:function fun3(){
      var d = 400
     }

}

这里注意的是fun函数是在全局的环境下产生的,所以自己身上挂载这一个go,由于作用域链是栈式结构,先产生的先进去,最后出来,

在这个例子的情况下,ao是后于go产生的,所以对于fun函数本身来说,执行代码的时候,会先去自己本身的ao里找找看,如果没有找到要用的东西,就去父级查找,此题的父级是go

此刻fun的作用域链是  第0位    fun的ao{}

          第1位    go{}

fun函数开始逐行执行ao{

  b:200

  fun2:function fun2(){
       var c = 300
     }

  fun3:function fun3(){
      var d = 400
     }

 }

注意:函数每次调用才会产生ao,每次产生的ao还都是不一样的

然后遇到fun2函数的执行,预编译产生自己的ao{

  c:underfined

}

此刻fun2的作用域链是第0位    fun2的ao{}

          第1位    fun的ao{}

          第2位    go{}

然后遇到fun3函数的执行,预编译产生自己的ao{

  d:underfined

}

此刻fun3的作用域链是第0位    fun3的ao{}

          第1位    fun的ao{}

          第2位    go{}

fun2和fun3的作用域链没有什么联系

当函数fun2和fun3执行完毕,自己将砍掉自己和自己的ao的联系,

最后就是fun函数执行完毕,它也是砍掉自己和自己ao的联系。

这就是一个我们平时看到不是闭包的函数。

4.闭包

1.闭包在红宝书中的解释就是:有权访问另一个函数作用域中的变量的函数。

2.写法:

 1 <script type="text/javascript">
 2     function fun1(){
 3         var a = 100;
 4         function fun2(){
 5             a++;
 6             console.log(a);
 7         }
 8         return fun2;
 9     }
10     
11     var fun = fun1();
12     fun()
13     fun()
14 </script>

3.效果如下:

JS之闭包

4.分析:

执行代码

go{

fun:underfined

fun1:function fun1()

   {

     var a = 100;

     function fun2()

    {

        a++;

        console.log(a);

     }

     return fun2;

     }

 

}

然后第十一行开始这里,就是fun1函数执行,然后把fun1的return赋值给fun,这里比较复杂,我们分开来看,

这里fun1函数执行,产生ao{

a:100

fun2:function fun2(){

    a++;
    console.log(a);
    }

}

此刻fun1的作用域链为 第0位   ao

           第1位   go

此刻fun2的作用域链为 第0位   fun1的ao

           第1位   go

解释一下,fun2只是声明了,并没有产生调用,所以没有产生自己的ao,

正常的,我们到第7行代码我们就结束了,但是这个时候来了一个return fun2,把fun2这个函数体抛给了全局变量fun,好了,fun1函数执行完毕,消除自己的ao,

此刻fun2的作用域链为 第0位   fun1的ao

           第1位   go

第十二行就是fun执行,然后,它本身是没有a的,但是它可以用fun1的ao,然后加,然后打印,

因为fun中的fun1的ao本来是应该在fun1销毁时,去掉,但是被抛给fun,所以现在fun1的ao没办法销毁,所以现在a变量相当于一个只能被fun访问的全局变量。

所以第十三行再调用一次fun函数,a被打印的值为102.

5.闭包之深入理解

举例1:

 1 <script type="text/javascript">
 2     function fun1(){
 3         var a = 100;
 4         function fun2(){
 5             a ++;
 6             console.log(a);
 7         }
 8         
 9         return fun2;
10     }
11     var fn1 = fun1();   //生成自己的ao,上面有a
12     var fn2 = fun1();
13     fn1()//101
14     fn1()//102
15     fn2()//101
16 </script>

fn1和fn2互不干涉,因为fun1函数调用了两次,所以两次的ao是不一样的。

举例2:

 1 <script type="text/javascript">
 2 function fun(){
 3     var num = 0;
 4     function jia(){
 5         num++;
 6         console.log(num);
 7     }
 8     function jian(){
 9         num--;
10         console.log(num)
11     }
12     return [jia,jian];
13 }
14 var fn = fun();
15 var jia = fn[0];
16 var jian = fn[1];
17 jia()//1
18 jian()//0
19 </script>

jia和jian共用一个fun的ao,一动全都动,十二行返回了一个数组,

举例3:

 1 <script type="text/javascript">
 2 function fun(){
 3     var num = 0;
 4     function jia(){
 5         num++;
 6         console.log(num);
 7     }
 8     function jian(){
 9         num--;
10         console.log(num)
11     }
12     return [jia,jian];
13 }
14 var jia = fun()[0];
15 var jian = fun()[1];
16 jia()//1
17 jian()//-1
18 </script>

 这里有一个坑,jia = fun()[0]; jian = fun()[1];fun函数执行了两遍,所以两次的ao不一样,所以jia和jian操作的对象不一样。

6.闭包好处与坏处

好处:

①保护函数内的变量安全 ,实现封装,防止变量流入其他环境发生命名冲突

②在内存中维持一个变量,可以做缓存(但使用多了同时也是一项缺点,消耗内存)

③匿名自执行函数可以减少内存消耗

坏处:

①其中一点上面已经有体现了,就是被引用的私有变量不能被销毁,增大了内存消耗,造成内存泄漏,解决方法是可以在使用完变量后手动为它赋值为null;

②其次由于闭包涉及跨域访问,所以会导致性能损失,我们可以通过把跨作用域变量存储在局部变量中,然后直接访问局部变量,来减轻对执行速度的影响

7.闭包解决的问题

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
    </head>
    <body>
        <ul>
            <li>0</li>
            <li>1</li>
            <li>2</li>
            <li>3</li>
            <li>4</li>
            <li>5</li>
            <li>6</li>
            <li>7</li>
            <li>8</li>
            <li>9</li>
        </ul>
        <script type="text/javascript">
            var lis = document.getelementsbytagname("li");
            for(var i = 0;i < lis.length;i++){
                lis[i].onclick = function(){
                    console.log(i)
                }
                
            }
        </script>
    </body>
</html>

JS之闭包

不管点击哪个都是10,那是因为点击事件是我们点击才触发的函数,等到触发的时候,i早就变成10跳出循环了,,这个时候我们就需要立即执行函数,创造了十个不同的作用域

解决方案:

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
    </head>
    <body>
        <ul>
            <li>0</li>
            <li>1</li>
            <li>2</li>
            <li>3</li>
            <li>4</li>
            <li>5</li>
            <li>6</li>
            <li>7</li>
            <li>8</li>
            <li>9</li>
        </ul>
        <script type="text/javascript">
            var lis = document.getelementsbytagname("li");
            for(var i = 0;i < lis.length;i++){
//                lis[i].onclick = function(){
//                    console.log(i)
//                }
                (function(i){
                    lis[i].onclick = function(){
                        console.log(i)
                    }
                })(i)
            }
        </script>
    </body>
</html>