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

JS闭包

程序员文章站 2022-06-21 16:26:55
什么是闭包? javascript和其他编程语言一样,也采用词法作用域,也就是说,函数的执行依赖于变量作用域.js函数对象的内部状态不仅包含函数的代密码逻辑,还必须引用当前的作用域。 函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性在计算机科学文献中称为“闭包 ......

什么是闭包?

  javascript和其他编程语言一样,也采用词法作用域,也就是说,函数的执行依赖于变量作用域.js函数对象的内部状态不仅包含函数的代密码逻辑,还必须引用当前的作用域。

  函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性在计算机科学文献中称为“闭包”。

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

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

            var fun = fun1();
            fun()
</script>

 

闭包的特点:

  1)占内存:当内部的函数被保存到了外面,就会形成闭包,闭包导致原有的函数执行完成了以后作用域链不会得到释放

造成了内存的泄露

  2)保护私有变量的安全

 

闭包的应用:

  1)计数器:

    由于每次访问的都是同一个作用域,在函数掉用完一次之后并没有被回,反而更新了它的作用域,所以函数每调用一次,它的值就会更新,次数就会增加一次。

function add(){
           var num = 0;
           return function(){
              num++;
             console.log(num)
            }
  }
  var fun = add();
  fun()              //1
  fun()              //2

  

  2)做缓存结构  

    每调用一次jia函数它就会加一,每调用一次jian函数它就会减一,

function fun(){
  var num = 0;
  function jia(){
       num++;
   console.log(num);
   }
  function jian(){
      num--;
    console.log(num)
   }
    return [jia,jian];
}
var jia = fun()[0];
var jian = fun()[1];
jia()          //1
jian()         //-1
jia()          //2
jian()         //-2
 

    

   3)封装

 

实例

 利用闭包,解决循环添加事件遇到的作用域问题:

 

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

 

  如上代码,获取li元素,然后为每个li循环添加一个单击事件,使单击同的li显示对应的类数组的索引。

 

  但实际上,却是这种情况:(如图)

JS闭包

   单击不同的li,控制台显示的都是10。

  这是因为,函数作用域的问题:

  1)先生成一个go

  2)预编译时

go = {
  lis : undefined
  i : undefined
   }
3)执行后
go = {
  lis : {}
  i : 10
}

4)函数调用后生成10个ao
   lis[i].ao(i = 0-9){  *10

}

   此时在lis[i]函数中并没有找到i,所以只能向上级寻找,所以找到上级的i为10。这也就是每次单击时输出都为10的原因了。

 

  现在我们用闭包来解决这个问题,把上面的那种方式注释,将其放到一个立即执行函数中,就形成了一个闭包。

<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){   //ec6之前解决的方法,有且只有这一种
                    lis[i].onclick = function(){
                        console.log(i)
                    }
                })(i)
            }
</scrip>

 

此时我们单击时就可以获取正确的索引值。

JS闭包  此时的生成的go是同之前的go 相同的,只是在执行立即函数时分别生成了10个ao,

 {

    i : 0 - 9
   }

  当再次触发单击事件时,单击函数寻找的对象就成了父级的ao的i,即立即执行函数的10个ao中的i。所以单击第i个就会寻找到与之对应的i的值,也就是不同li的索引值。