在理解闭包前理解JavaScript的作用域是有必要的,如果有兴趣,请移步"对JavaScript作用域的认识"
什么是闭包
闭包就是指有权访问另一个函数作用域中变量的函数,通俗点讲闭包就是能够读取其他函数变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。
1 function fn1() { 2 var a = 1 ; 3 function fn2() { 4 alert(a); 5 } 6 return fn2; 7 } 8 9 var result = fn1(); 10 result(); //1
上述函数fn2就是一个闭包,我们可以通过返回fn2获取到fn1中的变量。
闭包的用途
第一个用途就是上述通过闭包去访问函数内部的变量,第二个常见的用途就是使某些变量常驻内存
1 //没有闭包情况下变量a的情况 2 function add() { 3 var a = 1; 4 a++; 5 alert(a); 6 } 7 add(); //2 8 add(); //2 9 10 //有闭包的方式下 11 function add2() { 12 var a = 1; 13 function fn() { 14 a++; 15 alert(a); 16 } 17 return fn; 18 } 19 var result = add2(); 20 result(); //2 21 result(); //3
在有闭包的情况下,result()的两次输出分别是2,3,这说明aad2中的a变量一直在内存当中,并没有因为add2的调用后被垃圾回收机制回收(garbage collection)。
为什么会产生这样的情况?这是因为add2是fn的父亲函数,而在全局环境当中,fn其实是被赋给了全局变量result的,而result只会在全局环境执行完所有代码之后才会被回收,所以就导致了fn常驻内存,而fn的存在又依赖于父亲函数add2,所以add2在被调用之后也不会别GC回收,也始终存在于内存当中。
闭包可能会引发的问题
由于IE9之前的版本对JSript对象和COM对象使用了不同的垃圾回收机制,所以闭包在IE9之前的版本中会导致内存泄漏问题。具体来说就是,如果闭包中保存着一个HTML元素,那么这个元素将无法被销毁,直到退出程序或关闭浏览器。
1 <script> 2 window.onload = function () { 3 var oBox = document.getElementById('box'); 4 5 oBox.onclick = function () { 6 alert(oBox.id); 7 } 8 }; 9 </script>
像上述的情况下就发生了内存泄漏,因为在闭包中创建了一个循环引用。可以通过以下方法得到解决
1 <script> 2 window.onload = function () { 3 var oBox = document.getElementById('box'); 4 5 oBox.onclick = function () { 6 alert(oBox.id); 7 }; 8 9 //方法一 10 window.onunload = function () { 11 oBox.onclick = null; 12 } 13 }; 14 15 //方法二 16 window.onload = function () { 17 var oBox = document.getElementById('box'); 18 var id = oBox.id; 19 20 oBox.onclick = function () { 21 alert(id); 22 }; 23 24 oBox = null; 25 }; 26 27 </script>