javascript:闭包 JavaScript:做用域与做用域链 javascript预编译的过程

在阅读本篇文章以前,能够先参考个人JavaScript:做用域与做用域链javascript预编译的过程,能够更好的理解

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

咱们先总结一下什么是闭包:

  • 何时须要用到闭包:
    • 须要在一个函数外部,访问函数内部的变量的时候(也就是说在函数运行完以后,你想要把变量保存下来,待须要的时候调用。而不是经过垃圾回收机制消除(garbage collection))。
    • 保护变量安全。一个函数的内部变量,只能内部函数引用。
  • 如何定义闭包:
    • 在一个函数内部,定义一个函数,并返回一个函数的引用。

 

 

在外层函数中,嵌套一个内层函数,那么这个内层函数能够向上访问到外层函数中的变量。javascript

既然内层函数能够访问到外层函数的变量,那若是把内层函数return出来会怎样?html

function outer () {
    var thing = '吃早餐';
    
    function inner () {
        console.log(thing);
    }
    
    return inner;
}

var foo = outer();
foo();  // 吃早餐

函数执行完后,函数做用域的变量就会被垃圾回收。而这段代码看出当返回了一个访问了外部函数变量的内部函数,最后外部函数的变量得以保存。java

这种当变量存在的函数已经执行结束,但扔能够再次被访问到的方式就是“闭包”。跨域

1.闭包

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.效果以下:安全

4.分析:闭包

执行代码函数

GO{post

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.

2.闭包之深刻理解

举例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操做的对象不同。

3.闭包好处与坏处

好处:

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

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

③匿名自执行函数能够减小内存消耗

坏处:

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

②其次因为闭包涉及跨域访问,因此会致使性能损失,咱们能够经过把跨做用域变量存储在局部变量中,而后直接访问局部变量,来减轻对执行速度的影响

4.闭包解决的问题

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

无论点击哪一个都是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>
相关文章
相关标签/搜索