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>
浙公网安备 33010602011771号