关于闭包的一些知识

在了解闭包之前先得弄清楚之前讲的作用域的相关知识。

1、什么是闭包

  可以在外部通过某一种手段(方法)可以访问到内部的变量,这种方法就叫做闭包。

2、闭包用法的简单举例

  

function fun1(){
                var a = 100;
            }
            fun1()
            console.log(a);

  如上代码所示,依我们所学的知识我们此时想要打印出a的值应该不行的,因为a定义的作用域在函数内,全局的GO上是没有a这个变量的,故会报错。

  那么我们怎么样才能让它在外部打印出来呢

            function fun1(){
                var a = 100;
                function fun2(){
                    console.log(a);
                }
                return fun2;
            }
            
            var fun = fun1();
            fun()

  如上所示,我们先将我们所要执行的代码封装在一个函数内部的另一个函数里,然后返回这整个函数并在外部接受然后触发,这样就能做到我们想要的效果。

  那么,原理是什么呢?

    首先我们分析一下函数的作用域:

      在fun1执行时,生成fun1所对应的作用域,fun1.AO = {a:100,fun2:function}(此处省略分析过程)

      在fun2被返回执行的时候,生成fun2所对应的作用域,fun2.AO = { }

    这时关键点在于,返回出来的fun2这整个函数就算返回到了fun1外面,但是fun2的父级依然还是fun1,那么在fun2的AO中找不到a对应的值,就返回父级的AO中寻找其所对应的值,而fun1的AO中有a,值为100。

    所以这是fun2被返回调用后执行打印出来的结果就是100。

3、那么我们再看一个情况:

            function fun1(){
                var a = 100;
                function fun2(){
                    a ++;
                    console.log(a);
                }
                
                return fun2;
            }
                        var fun = fun1();
                        fun()
                        fun()

  如上所示,根据之前的知识,我们知道这里当fun第一次调用时打印的结果为101,那么当fun第二次调用的时候呢?

  因为fun2的AO中并没有定义a,所以fun2调用时改变的a的值是在其父级也就是fun1的AO中改变a的值,所以当fun第二次被调用的时候,此时fun1中a的值已经变为101,故第二次调用完后打印出来的值为102。

4、这里我们再讨论一种做法:

        <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>

  如上所示,我们想要点击某一个数时后台打印出其对应的数字,粗略来看,上述逻辑好像并没有什么问题,但不管点击什么打印出来都只是10.

  那么这是为什么呢?

    因为在我们点击之前,全局已经生成了作用域GO,此时因为循环已经完成,生成的GO中变量i的最后赋值肯定是为10,然而当我们点击触发时间函数时所生成的作用域AO中没有关于i的定义,只能返回父级寻找,所以这时找到的i值就为10.

  

  那么怎么解决呢?

        <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++){
                (function(i){   
                    lis[i].onclick = function(){
                        console.log(i)
                    }
                })(i)
            }
                </script>

    如上所示,将点击事件函数装在一个立即执行函数中,形参实参都为i,这时每一次循环所生成的函数参数都为列表所对应的值,那么这所生成的所有函数的AO中都会有其所对应的i值,而这些函数正是我们点击事件函数的父级,所以这时我们再去点击触发时返回父级所寻找的i值即为我们想要的值。

以上简单的列举了闭包的一些简单用法,闭包具体的应用还有很多很多,学无止境。

  

原文地址:https://www.cnblogs.com/lznzxy/p/10138593.html