day-8.6 函数的闭包

在说闭包之前,需要了解一个概念。

即JS的内存回收机制,因为闭包可以说是对JS内存回收的空制。

内存回收机制:

所有程序代码,都是通过变量字符(var 和 function 带的名字)去利用内存去存储数据,然后通过CPU调用内存的数据去运行的,

比如,浏览器打开一个网站,当解析执行到<script>这个标签的时候,他就会在内存中,以变量名,创建一个储存数据的内存空间。当解析执行到 函数执行fn ()的时候,他就会开始执行读取fn 这个函数后面的函数体内容。

去开辟一个子作用域的内存空间,去存储这个子作用域的fn 函数的变量名的数据等,当执行到这个函数代码的结束花括号的时候,就会把这个子作用域的内存空间给释放掉。

内存是固定的,如果每个程序在执行的时候都往里面去存,而不做释放的话,久了内存就会溢出卡死。

所以,所谓的内存回收机制就是数据对内存占用的回收。

JS的内存回收,除了全局的变量的数据之外(只有关掉网页才会被回收,这个是涉及到浏览器的这个程序的回收机制的优化,对于JS来说,只要网站不打开,全局的变量就一直存在。),其他的作用域的变量的数据在执行结束后,都会被释放。

比如:

1 var a = {"name":"guo";"age":18};  //全局的a除非网站关闭,否则永远不会被回收。
2 function fn(){
3   var a = 5;
4   alert(a);    
5 }
6 fn (); //fn作用域的a ,fn执行完后,如果没有被引用,fn作用域里的a就会从内存释放掉。

如上例,当全局变量是一个引用型的时候,为了避免造成内存占用,一般在该变量结束引用的时候,将该变量赋值为null,即 在后面加一个a = null,或者将所有这段代码放在一个自执行的函数体内,当代码运行结束后,会自动释放掉内存。

一般,函数作用域里的变量,函数作用域外面,即全局代码是不能对函数作用域里的变量去进行引用的。

既然全局作用域的变量是不会被释放的,那我们可以通过在全局对子作用域的引用,就能实现子函数作用域的变量在 子函数执行完后仍不被回收,这样的做法就叫做,闭包

关于引用:通常来说,外部的作用域是不能对子作用域里面的变量进行引用的。

1 function a (){
2   var x = 10;  
3 }
4 function b (){
5   //code  这里无法对函数a的x进行引用。
6 }
7 //这里也不可以。

 

对一个子函数作用域的变量引用的方式有以下几种方式;

举例1  return:

1 function fn (){
2  var a = 10;
3   console.log(a);
4   return a ;     //通过return
5 }
6 var a = fn(); //获取return的值,访问到fn函数里面的变量a
7 alert(a); //弹出10

举例2 赋值;

1 function fn() {
2                 var a = 10;
3                 console.log(a); 
4                 b = a;  //赋值修改一个作用域和全局都没有的变量,全局会自动创建一个该变量并赋值
5             }
6             fn();
7             alert(b);  //全局有了b,可以弹出b

上面几种方式并不是真正对子函数作用域里面的变量进行访问,而是通过return或者转存的方式复制了一份给全局的变量存储而已。正常情况下不是都要使用闭包的,只要能满足需求,可以不用闭包。

闭包的概念:闭包就是能够读取其他函数内部变量的函数

通常我们创建闭包的方式是一个函数嵌套一个内部函数,内部函数对外部函数的变量进行引用,再将内部函数return出去。

闭包举例:

 1 function fn (){
 2                 var a = 10;
 3                 alert(a);
 4                 return function () {  //未命名的函数就是数据,可以被返回,这里返回的函数给b,这个函数对函数fn里面的变量a进行了引用。这个时候这种引用就称之为闭包。
 5                     a ++;   
 6                     alert(a);
 7                 }
 8             }
 9             var b = fn();  //输出10;
10                 b (); //输出11;
11                 b (); //输出12;
12                 fn(); //输出10;

这个时候变量a 还是 一个局部变量,但是他不像其他局部变量那样会被回收:

1  function fn (){
2                 var a = 10;
3                 a++;
4                 alert(a);
5 
6             }
7             fn();  //输出11,每一次执行完后都会被回收;
8             fn();  //输出11,每一次执行完后都会被回收;

闭包之后,这个变量虽然还是一个局部变量,但是他可以像一个全局变量一样不会被回收;

1 <script>
2     var a = 10;
3             a++;
4             alert(a);  //输出11;这时候a已经是11了;
5             a++;  //11自增1,变成12,
6             alert(a);  //输出12,
7 </script>

闭包和上面的全局变量的效果一样,但是他不会成为一个全局的变量,而是一个局部的变量,虽然他具有全局变量的特性。

上面的例子还引出一个概念,即,函数的作用域在哪取决于在哪里定义,而不是在哪里执行。即函数的代码是在哪,而不是执行代码在哪,上述例子b();执行的时候是在全局,而函数的代码是在函数fn内,所以他的作用域是在fn内,而不是在全局。

闭包的最常见的应用

非闭包的写法

 1 <body>
 2         <div id="wrap">
 3             <p>000</p>
 4             <p>111</p>
 5             <p>222</p>
 6             <p>333</p>
 7         </div>
 8         
 9         <script>
10 
11             var aP = document.getElementsByTagName("p"),
12                 len = aP.length;
13 
14             for(var i = 0;i <len; i++){
15                     ap[i].onclick = function (){
16                     this.index = i ;
17                     alert(this.index);
18             }    
19             }
20         </script>
21     </body>    

闭包的写法

 1 <body>
 2         <div id="wrap">
 3             <p>000</p>
 4             <p>111</p>
 5             <p>222</p>
 6             <p>333</p>
 7         </div>       
 8         <script>
 9 
10             var aP = document.getElementsByTagName("p"),
11                 len = aP.length;
12 
13             for (var i=0;i<len;i++){
14                 !function(i){
15                     aP[i].onclick = function () {  //ap[i].onclick 是一个全局的系统事件,这里其实跟return 自执行的function 里面的函数出去给一个全局的变量是一样的。
16                         alert(i);
17                     };
18                 }(i);
19             }
20         </script>
21 </body>

 

posted @ 2018-06-10 17:37  bibiguo  阅读(82)  评论(0)    收藏  举报