1、闭包改变了变量的作用域,使得函数内的变量,能在函数外部调用;
2、闭包改变了函数内变量的生命周期。本来函数内的变量在函数结束之后就销毁了,但闭包使得函数内的变量一直存在,直到页面关闭;
3、
闭包是一个什么概念呢?先来看下面一段代码:
<html> <head> <title>js闭包</title> <script type="text/javascript"> window.onload = function(){ var divs=document.getElementsByTagName("div"); for(var i=0; i<divs.length; i++){ divs[i].onclick=function(){ alert(i); //每次都输出5 } } } </script> </head> <body> <div style="background-color:#111; 100px; height:100px"></div> <div style="background-color:#333; 100px; height:100px"></div> <div style="background-color:#555; 100px; height:100px"></div> <div style="background-color:#777; 100px; height:100px"></div> <div style="background-color:#999; 100px; height:100px"></div> </body> </html
如果要改为弹出正确的序号(从0开始)则js要改为如下这样:
<script type="text/javascript"> function A(){ var divs=document.getElementsByTagName("div"); for(var i=0; i<divs.length; i++){ (function(n){ //实际的思路其实是,对于每个onclick的内部函数,都有了一个父变量n。并且内存不能销毁 divs[i].onclick=function(){ alert(n); } })(i) } } window.onload = function() { A(); } </script>
这据说是闭包这个牛B词的实现。
其实,他写的太复杂了,实际上相当于这样写,而实际上在真正的开发中,下面的代码风格也是比较好的。
<script type="text/javascript"> var divs; function A(){ divs = document.getElementsByTagName("div"); for(var i=0; i<divs.length; i++){ bindclick(i) } } function bindclick(i) //参数相当于内部变量,每调用一次都有一个独立的内部变量 { divs[i].onclick = function(){ alert(i); } } window.onload = function() { A(); } </script>
哈,这样就清晰多了。效果一样。
下面再给出一个无论点击哪个先哪个后都弹出1 2 3 4 5
<script type="text/javascript"> function A(){ var divs=document.getElementsByTagName("div"); var j = 0; for(var i=0; i<divs.length; i++){ divs[i].onclick=function(){ alert(j); j++; } } } window.onload = function() { A(); } </script>
要说明这个原因,首先要理解了内部函数的概念!
一、什么是内部函数
一个示例说明:
function A(){ function B(){ //B就是一个内部函数 alert("你好!"); } }
由于函数B在函数A的内部,因此B函数在函数A内有效,但是在外部调用时无效的,如:
<script type="text/javascript"> window.onload = B(); //外部调用无效 function A(){ alert("你好"); function B(){ //B就是一个内部函数 alert("你好!"); } } </script>
在js报如下错误:
但是在内部调用又是正确的:
<script type="text/javascript"> window.onload = A(); function A(){ function B(){ alert("你好!"); } B(); //内部调用A()是正确的 } </script>
不能在外部调用内部函数,这我很不爽,有没有办法硬是要在外部调用内部的函数呢?答案是有的:
方法一、全局变量绑定函数
<script type="text/javascript"> window.onload = A(); function A(){ function B(){ alert("你好!"); } global = B; } global(); //全局变量来调用内部函数 </script>
虽然可以调用B()了,但是必须使用全局变量global,直接在外部调用B()一样出错。
方法二、通过返回值调用函数
<script type="text/javascript"> function A(){ function B(){ alert("你好!"); } return B; } window.onload = function(){ var fn = A(); //返回内部函数的引用 fn(); //调用引用 } </script>
javascript运行时需要跟踪引用这个内部函数的所有变量,直到最后一个变量废弃,javascript垃圾收集器才能释放内部函数的内存空间。也就是说,只要存在调用内部函数的可能,javascript就要保留被引用的函数。
2014-04-14,这里要特别说明一下:
function f1() { r = 10; } function f1() { var r = 10; }
以上两个方法是不同的,加了var就是局部变量,不加var就是全局变量。
二、内部函数与闭包的关系
闭包是指有权访问另一个函数作用域的变量(包括变量与函数等等)的函数,创建闭包的常见方式则是在一个函数内部创建另一个函数。
变量的作用域
<script type="text/javascript"> function A(){ function B(){ var i = 0; //B的内部变量 i++; alert(i); } return B; } window.onload = function(){ var fn = A(); fn(); //弹出1 fn(); //弹出1 var fn2 = A(); fn2(); //弹出1 fn2(); //弹出1 } </script>
因为每次通过引用调用B()函数,都会创建一个 i 变量,并且++后值为1。
<script type="text/javascript"> var i = 0; //全局变量 function A(){ function B(){ i++; alert(i); } return B; } window.onload = function(){ var fn = A(); fn(); //弹出1 fn(); //弹出2 var fn2 = A(); fn2(); //弹出3 fn2(); //弹出4 } </script>
因为调用的是全局变量i,所以每次++,+的都是同一个。
如果调用的是父函数的局部变量,也类似:
<script type="text/javascript"> function A(){ var i = 0; //父函数的局部变量 function B(){ i++; alert(i); } return B; } window.onload = function(){ var fn = A(); fn(); //弹出1 fn(); //弹出2 var fn2 = A(); fn2(); //弹出1 fn2(); //弹出2 } </script>
因为是变量时父函数的变量。因此,调用A()的时候都var i = 0;所以是从0开始。
说白了闭包就是用些什么乱七八糟的方法去访问函数内部的变量或函数。
三、闭包的作用
闭包有两个作用:
- 函数外部访问函数内部的函数或变量;
- 常驻内存;
来看示例:
function f1(){ var n=999; nAdd=function() { n += 1; } function f2(){ alert(n); } return f2; } var result=f1(); result(); // 999 nAdd(); result(); // 1000
result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。
为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量(result),这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。