闭包

是指在一个函数中声明一个函数,在外部函数结束的时候返回内部函数的引用,那么在执行外部函数的时候就会

产生一个内部函数的引用,因为外部函数执行完成,理论上外部函数的ao的引用是应该被删除或者说释放的。

但因为有了内部函数的引用,这个内部函数在声明的时候就已经指向了外部函数的ao以及go,所以,即使外部函数

ao的引用已经释放,由于内部函数被保存在了外部,导致外部函数申明与执行所产生的ao被保留下来。

<body>
  <script type="text/javascript">
    function a(){
      function b(){
        var bbb = 234;
        consloet.log(aaa);
      };
        var a = 123         
return b;     }     var golb = 100;     var demo = a();     demo(); </script> </body>

此例的输出结果是:123 

按原来的理解,b函数中是没有a这个变量的,正常如果没有外部的a函数,就会报错 a is not define

但是这里,由于在a执行完的时候,把b的引用保存在了函数a之外,而b这个函数在声明的时候,它的scope chain仍然和a执行是同一个,

即:0:aAO;1:GO,就是把a中的执行上下文保存在了b函数中。在执行函数b的时候回生成一个bAO放在scope chain的0位置,执行完的

时候删除这个bAO,而在1、2位置的0:aAO;1:GO被永久的保留了下来。

aAO中是由a = 123这个变量的,故而最终的结果是打印出a的值!~

 

这 就是闭包!

**注意点**------当内部函数被保存到外部时,将会生成闭包。闭包会导致原有scope chain(作用域链)不释放,造成内存泄漏

      内存泄漏:由于内存一直占用,导致可用内存空间减小,感觉就像内存泄漏了一样,就叫内存泄漏了~~!会导致加载非常慢!!

 

scope chain 中的没生成一个新的ao,必然继承外部函数产生的ao与go,这里的继承是指指向了原来那个AO、GO,而不是生成一个一模一样的

ex:

<body>
  <script type="text/javascript">
    function text(){
      var num = 100;
                  function b(){
                       num ++;
                       console.log(num);    
                  }
                 function b(){
                      num --;
                      console.log(num);
                 }
                  return [a,b];
    }
    var myArr = text();
    myArr[0]();
    myArr[1]();
</script>
</body>                                                       

输出的结果是:101  100  

而不是101  99,注意其中的区别!

  所以再修改来自外部函数的ao时,是修改的同一个AO

ex:

<body>
  <script type="text/javascript">
    function a(){
      var num = 100;
                  function b(){
                       num ++;
                       console.log(num);    
                  }
                  return b;
    }
    var demo = a();
    demo();
    demo();
</script>
</body>                        

此例的结果是:101

       102

有累加的效果

 

闭包的作用:1实现共有变量----->函数累加器

                    2可以做缓存(存储结构)----->eater

      3可以实现封装,属性私有化------>Person();

      4模块化开发,防止污染全局变量

ex: 实现共有变量----->函数累加器

<body>
  <script type="text/javascript">
    function add(){
            var count = 0;
                        function demo(){
        count ++;
        consloet.log(count);
      };
     
        return demo;
    }
    var counter = add();
    
    counter();
            counter();   
            counter();   
            counter();   
            counter();   
            counter();   
            counter();   
            counter();   
</script>
</body>                

输出结果: 1 2 3 4 5 6 7 

 

ex:可以做缓存(存储结构)----->eater   3

<body>
  <script type="text/javascript">
    function eater(){
      var food = "";
                  var obj = {
                        eat : function(){
                        console.log("i am eating!" + food);
                        food = "";
                        },
                        push : function(myfood){
                                food = myfood;
                        }
                  }
                  return obj;
              }
      var demo = a();
      erter1.push('banana');
      eater1.eat();
</script>
</body>         

输出结果; i am eating banana

其中实际有两个过程,一个把banana放入myfood,第二个过程才是打印 i am eating banana

 立即执行函数:js提供的唯一一个可以立即销毁函数的东西

       功能:除了执行完就被释放之外和普通函数没有任何区别(有参数,有返回值(返回值又一个变量保存,类似变量的赋值),有预编译,作用域......)

       有些函数出生只是执行一次,执行完就没必要保留,因此需要立即执行函数,可以优化内存。比如初始化功能;计算某个参数的值,得出后就不再使用此函数,此函数只被执行一次。

语法:括号中放一个匿名函数(没有函数名的函数或者说不需要函数名的函数)

(function (){}())

ex:

(function (){
      var a = 123;
      var b = 234;
      console.log(a + b);
})    

匿名函数深入:

匿名函数其实不是js可以做出来的,而是人们在使用函数的时候不经意发现的一种用法,类似css中的float,出生的时候并不是为了浮动的,而只是为了文字环绕图片这个功能,只是在人们的使用中发现了一个更有价值的功能。

匿名函数并不是明文规定的语法,只是利用了()的性质特点(有执行的作用,也有运算符优先级的作用---先算小括号内的)

官方写法:(function(){}());W3C组织建议这种

      (function (){})();

只有表达式才能被执行符号:()执行

函数申明与函数表达式是俩东西:

函数申明:function test (){}

函0数表达式:var fun = function(){}

为什么function test (){}后面加一对括号不能使用呢?

       因为function test (){}虽然和表达式一样,但它叫“函数声明”

var fun = function(){}在后面加一对括号:var fun = function(){}()就能被执行

能被执行符号执行的表达式,这个函数的名字就会被自动忽略

如:var fun = function(){}()在执行之后,在使用fun,就会报错:undefined,也就是不再是一个函数

 

函数申明前面加一个正号“+”

+ function test(){

      console.log("a");

}

这个照样能被执行,加负号“-"(这里不能看成加号减号),逻辑与“&&” 逻辑或“||”前面得加东西,逻辑非“!”都是可以的。

注意:乘号除号是不行的

原因

一个表达式在前面加了一个运算符,在趋势上就会把它理解成数字,也就理解成表达式了,类似数学中的a=x

理解成一个赋值,而a = x+3就可以理解成一个表达式;

(function test (){

        console.log("a");

})()

这个也照样能被执行

(function test (){

        console.log("a");

}())

这个照样能被执行,原因是:先看最外层的()

一个表达式在前面加了一个运算符,在趋势上就会把它理解成数字,也就理解成表达式了,类似数学中的a=x

理解成一个赋值,而a = x+3就可以理解成一个表达式;

,js就会把它当做一个

阿里巴巴笔试题:变态恶心

function test (a,b,c,d){

        console.log(a+b+c+d);

}(1,2,3,4);

这个题看着理论上是坚决不能执行的,但是!!js会尽量识别成能不报错的样子:

function test (a,b,c,d){

        console.log(a+b+c+d);

}

 

(1,2,3,4);

上下部分分家;虽然不报错也不会执行,没意义。

 ************************闭包的解决************************

 

 

ex:

<body>
  <script type="text/javascript">
    function test(){
      var arr = [];
                  for(var i = 0, i<10;i++){
                        arr[i] = function(){
                             console.log(i);
                        }
                  }
                  return obj;
              }
      var myArr = test();
      for(var j = 0; j < 10; j++){
                   myArr[j]();
              }
</script>
</body>       

此例输出的结果为:10 10 10 10 10 10 10 10 10 10 

原因为闭包中保存出来的函数每一个函数都拥有外部函数test的AO,并且是同一个AO,也就是说每一个函数修改的都是同一个i值,当数组中的是个函数形成的时候,经过for循环i已经变成了10,因此在调用myArr里面的函数的时候,打印出来的全部是十。

一对十

 

问题来了,如果我们想要的结果如果是:0 1 2 3 4 5 6 7 8 9 就需要解决上面的问题。

<body>
  <script type="text/javascript">
    function test(){
      var arr = [];
                  for(var i = 0, i<10;i++){
                        arr[i] = function(){
                             console.log(i);
                        }
                  }
                  return obj;
              }
      var myArr = test();
      for(var j = 0; j < 10; j++){
                   myArr[j]();
              }
</script>
</body>      

 

超级简单粗暴的方法:

</!DOCTYPE html>
<html lang="en">
<head>
        
    <title>这是一个闭包    </title>
</head>
<body>
    <script type="text/javascript">
        function test(){
            var arr = [];
            for (var i = 0; i < 10; i++) {
                arr[i] = function(){
                    //console.log(i);
                    document.write(j);
                }
            }
            return arr;
        }
        var myArr = test();
        for (var j = 0; j < 10; j++) {
            myArr[j]();
        }

    </script>
        

</body>
</html>

       自己想的原因是:因为闭包只是保存了myArr这个包含十个函数的数组,里面的函数是定义好的,函数内部包含一个未声明的变量,本来在执行的时候应该会报错 j is not define。但我们在形成闭包的时候并不执行函数,所以不存在报错。

      而在外部使用这些函数的时候,由于for循环中有变量j,故而在执行的时候生成的ao里面产生了j这个变量,所以产生了这个结果~~

太有意思了,哈哈哈

正经方法:使用立即执行函数

</!DOCTYPE html>
<html lang="en">
<head>
        
    <title>这是一个闭包    </title>
</head>
<body>
    <script type="text/javascript">
        function test(){
            var arr = [];
            for (var i = 0; i < 10; i++) {
                (function(j){
                    arr[j] = function(){
                        console.log(j);
                    }
                }(i))
            }
            return arr;
        }
        var myArr = test();
        for (var j = 0; j < 10; j++) {
            myArr[j]();
        }

    </script>
        

</body>
</html>
View Code

理解:闭包是由于循环的时候i在变化中无法直接保存到函数外部,导致最后十个函数打印出的都是10

   那么解决的思路就是,在循环进行的时候,直接把函数保存成打印循环进行时候具体的每个i的值

          方法,用一个立即执行函数接收循环变量i,把这个i的值传入arr中的函数内部,那么在闭包形成后,

myArr中的每个函数打印的结果都不是10了,而是从0-9的数字。